public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
From: Richard Biener <richard.guenther@gmail.com>
To: Jonathan Wakely <jwakely@redhat.com>
Cc: libstdc++@gcc.gnu.org, gcc-patches@gcc.gnu.org
Subject: Re: [PATCH] libstdc++: Allow emergency EH alloc pool size to be tuned [PR68606]
Date: Mon, 10 Oct 2022 08:17:48 +0200	[thread overview]
Message-ID: <CAFiYyc2mt3e1Rd=g3B2_zzaJ0DfyJDAt5GPFVQg3OqrZx=NaUw@mail.gmail.com> (raw)
In-Reply-To: <20221007155452.1299670-1-jwakely@redhat.com>

On Fri, Oct 7, 2022 at 5:55 PM Jonathan Wakely via Gcc-patches
<gcc-patches@gcc.gnu.org> wrote:
>
> This needs a little more documentation (see the TODO in the manual),
> rather than just the comments in the source. This isn't final, but I
> think it's the direction I want to take.
>
> -- >8 --
>
> Implement a long-standing request to support tuning the size of the
> emergency buffer for allocating exceptions after malloc fails, or to
> disable that buffer entirely.
>
> It's now possible to disable the dynamic allocation of the buffer and
> use a fixed-size static buffer, via --enable-libstdcxx-static-eh-pool.
> This is a built-time choice that is baked into libstdc++ and so affects
> all code linked against that build of libstdc++.
>
> The size of the pool can be set by --with-libstdcxx-eh-pool-obj-count=N
> which is measured in units of sizeof(void*) not bytes. A given exception
> type such as std::system_error depends on the target, so giving a size
> in bytes wouldn't be portable across 16/32/64-bit targets.
>
> When libstdc++ is configured to use a dynamic buffer, the size of that
> buffer can now be tuned at runtime by setting the GLIBCXX_TUNABLES
> environment variable (c.f. PR libstdc++/88264). The number of exceptions
> to reserve space for is controlled by the "glibcxx.eh_pool.obj_count"
> and "glibcxx.eh_pool.obj_size" tunables. The pool will be sized to be
> able to allocate obj_count exceptions of size obj_size*sizeof(void*) and
> obj_count "dependent" exceptions rethrown by std::rethrow_exception.
>
> With the ability to tune the buffer size, we can reduce the default pool
> size. Most users never need to throw 1kB exceptions in parallel from
> hundreds of threads after malloc is OOM.

But does it hurt?  Back in time when I reworked the allocator to be less
wasteful the whole point was to allow more exceptions to be in-flight
during OOM shutdown of a process with many threads.

So if we reduce the default buffer size that should be documented
in changes.html, maybe with a hint how to restore the old buffer size
(configury flags required or runtime ENV setting)?

Otherwise looks OK to me.

Thanks,
Richard.

> The users who do need that can
> use the tunables to select larger sizes. The old default can be chosen
> with:
>
> For 64-bit:
> GLIBCXX_TUNABLES="glibcxx.eh_pool.obj_count=64:glibcxx.eh_pool.obj_size=112"
> For 32-bit:
> GLIBCXX_TUNABLES="glibcxx.eh_pool.obj_count=32:glibcxx.eh_pool.obj_size=112"
>
> libstdc++-v3/ChangeLog:
>
>         PR libstdc++/68606
>         * Makefile.in: Regenerate.
>         * acinclude.m4 (GLIBCXX_EMERGENCY_EH_ALLOC): New macro.
>         * configure: Regenerate.
>         * configure.ac: Use GLIBCXX_EMERGENCY_EH_ALLOC.
>         * crossconfig.m4: Check for secure_getenv.
>         * doc/Makefile.in: Regenerate.
>         * doc/xml/manual/configure.xml: Document new configure options.
>         * doc/xml/manual/using_exceptions.xml: Document emergency
>         buffer.
>         * doc/html/*: Regenerate.
>         * include/Makefile.in: Regenerate.
>         * libsupc++/Makefile.am: Use EH_POOL_FLAGS.
>         * libsupc++/Makefile.in: Regenerate.
>         * libsupc++/eh_alloc.cc (EMERGENCY_OBJ_SIZE): Define in units
>         of sizeof(void*) not including the ABI's exception header.
>         (EMERGENCY_OBJ_COUNT): Define as target-independent calculation
>         based on word size.
>         (MAX_OBJ_COUNT): Define macro for upper limit on pool size.
>         (pool) [_GLIBCXX_EH_POOL_STATIC]: Use fixed-size buffer.
>         (pool::buffer_size_in_bytes): New static member function.
>         (pool::pool): Parse GLIBCXX_TUNABLES environment variable to set
>         pool size at runtime.
>         (pool::in_pool): Use std::less<void*> for total order.
>         (__freeres) [_GLIBCXX_EH_POOL_STATIC]: Do nothing.
>         (__cxa_allocate_exception, __cxa_free_exception)
>         (__cxa_allocate_dependent_exception)
>         (__cxa_free_dependent_exception): Add [[unlikely]] attributes.
>         * po/Makefile.in: Regenerate.
>         * python/Makefile.in: Regenerate.
>         * src/Makefile.in: Regenerate.
>         * src/c++11/Makefile.in: Regenerate.
>         * src/c++17/Makefile.in: Regenerate.
>         * src/c++20/Makefile.in: Regenerate.
>         * src/c++98/Makefile.in: Regenerate.
>         * src/filesystem/Makefile.in: Regenerate.
>         * src/libbacktrace/Makefile.in: Regenerate.
>         * testsuite/Makefile.in: Regenerate.
> ---
>  libstdc++-v3/Makefile.in                      |   1 +
>  libstdc++-v3/acinclude.m4                     |  45 ++++
>  libstdc++-v3/configure                        |  67 +++++-
>  libstdc++-v3/configure.ac                     |   3 +
>  libstdc++-v3/crossconfig.m4                   |   1 +
>  libstdc++-v3/doc/Makefile.in                  |   1 +
>  libstdc++-v3/doc/html/index.html              |   2 +-
>  libstdc++-v3/doc/html/manual/configure.html   |  10 +-
>  libstdc++-v3/doc/html/manual/index.html       |   2 +-
>  libstdc++-v3/doc/html/manual/intro.html       |   2 +-
>  libstdc++-v3/doc/html/manual/using.html       |   2 +-
>  .../doc/html/manual/using_exceptions.html     |  53 +++-
>  libstdc++-v3/doc/xml/manual/configure.xml     |  23 ++
>  .../doc/xml/manual/using_exceptions.xml       |  48 ++++
>  libstdc++-v3/include/Makefile.in              |   1 +
>  libstdc++-v3/libsupc++/Makefile.am            |   2 +-
>  libstdc++-v3/libsupc++/Makefile.in            |   3 +-
>  libstdc++-v3/libsupc++/eh_alloc.cc            | 226 +++++++++++++-----
>  libstdc++-v3/po/Makefile.in                   |   1 +
>  libstdc++-v3/python/Makefile.in               |   1 +
>  libstdc++-v3/src/Makefile.in                  |   1 +
>  libstdc++-v3/src/c++11/Makefile.in            |   1 +
>  libstdc++-v3/src/c++17/Makefile.in            |   1 +
>  libstdc++-v3/src/c++20/Makefile.in            |   1 +
>  libstdc++-v3/src/c++98/Makefile.in            |   1 +
>  libstdc++-v3/src/filesystem/Makefile.in       |   1 +
>  libstdc++-v3/src/libbacktrace/Makefile.in     |   1 +
>  libstdc++-v3/testsuite/Makefile.in            |   1 +
>  28 files changed, 414 insertions(+), 88 deletions(-)
>
> diff --git a/libstdc++-v3/Makefile.in b/libstdc++-v3/Makefile.in
> index 58a0acdcc1b..a7c2b60678b 100644
> --- a/libstdc++-v3/Makefile.in
> +++ b/libstdc++-v3/Makefile.in
> @@ -240,6 +240,7 @@ ECHO_C = @ECHO_C@
>  ECHO_N = @ECHO_N@
>  ECHO_T = @ECHO_T@
>  EGREP = @EGREP@
> +EH_POOL_FLAGS = @EH_POOL_FLAGS@
>  ERROR_CONSTANTS_SRCDIR = @ERROR_CONSTANTS_SRCDIR@
>  EXEEXT = @EXEEXT@
>  EXTRA_CFLAGS = @EXTRA_CFLAGS@
> diff --git a/libstdc++-v3/acinclude.m4 b/libstdc++-v3/acinclude.m4
> index 719eab15c77..6523ba9a816 100644
> --- a/libstdc++-v3/acinclude.m4
> +++ b/libstdc++-v3/acinclude.m4
> @@ -5092,6 +5092,51 @@ BACKTRACE_CPPFLAGS="$BACKTRACE_CPPFLAGS -DBACKTRACE_ELF_SIZE=$elfsize"
>    GLIBCXX_CONDITIONAL(ENABLE_BACKTRACE, [test "$enable_libstdcxx_backtrace" = yes])
>  ])
>
> +dnl
> +dnl Allow the emergency EH pool to be configured.
> +dnl
> +dnl --enable-libstdcxx-static-eh-pool will cause a fixed-size static buffer
> +dnl to be used for allocating exceptions after malloc fails. The default is
> +dnl to allocate a buffer using malloc
> +dnl
> +dnl --with-libstdcxx-eh-pool-obj-count=N will set the default size for the
> +dnl buffer. For a static buffer that size is fixed permanently. For a dynamic
> +dnl buffer it's the default, but it can be overridden from the environment.
> +dnl
> +dnl To set the default to approximately the same values as GCC 12,
> +dnl use --with-libstdcxx-eh-pool-obj-count=94 for 32-bit targets,
> +dnl and --with-libstdcxx-eh-pool-obj-count=252 for 64-bit targets.
> +dnl
> +dnl Defines:
> +dnl  _GLIBCXX_EH_POOL_STATIC if a fixed-size static buffer should be used
> +dnl  instead of allocating a buffer on startup.
> +dnl  _GLIBCXX_EH_POOL_NOBJS to override the default EMERGENCY_OBJ_COUNT value.
> +dnl
> +AC_DEFUN([GLIBCXX_EMERGENCY_EH_ALLOC], [
> +  eh_pool_static=
> +  eh_pool_nobjs=
> +  AC_ARG_ENABLE([libstdcxx-static-eh-pool],
> +    AC_HELP_STRING([--enable-libstdcxx-static-eh-pool],
> +                  [use a fixed-size static buffer for allocating exceptions if malloc fails]),
> +    [if test "${enableval}" = yes; then
> +      eh_pool_static="-D_GLIBCXX_EH_POOL_STATIC"
> +      AC_MSG_NOTICE([EH pool using static buffer])
> +    fi],)
> +
> +  AC_ARG_WITH([libstdcxx-eh-pool-obj-count],
> +    AC_HELP_STRING([--with-libstdcxx-eh-pool-obj-count],
> +                  [the number of exceptions that can be allocated from the pool if malloc fails]),
> +    [if test "${withval}" -ge 0 2>/dev/null; then
> +      eh_pool_obj_count="-D_GLIBCXX_EH_POOL_NOBJS=${withval}"
> +      AC_MSG_NOTICE([EH pool object count: ${withval}])
> +    else
> +      AC_MSG_ERROR([EH pool obj count must be a non-negative integer: $withval])
> +    fi],)
> +
> +  EH_POOL_FLAGS="$eh_pool_static $eh_pool_obj_count"
> +  AC_SUBST(EH_POOL_FLAGS)
> +])
> +
>  # Macros from the top-level gcc directory.
>  m4_include([../config/gc++filt.m4])
>  m4_include([../config/tls.m4])
> diff --git a/libstdc++-v3/configure.ac b/libstdc++-v3/configure.ac
> index c05fcdda7e9..42c453099f2 100644
> --- a/libstdc++-v3/configure.ac
> +++ b/libstdc++-v3/configure.ac
> @@ -538,6 +538,9 @@ GLIBCXX_CHECK_SIZE_T_MANGLING
>  # Check which release added std::exception_ptr for the target
>  GLIBCXX_CHECK_EXCEPTION_PTR_SYMVER
>
> +# For libsupc++/eh_alloc.cc defaults.
> +GLIBCXX_EMERGENCY_EH_ALLOC
> +
>  # Define documentation rules conditionally.
>
>  # See if makeinfo has been installed and is modern enough
> diff --git a/libstdc++-v3/crossconfig.m4 b/libstdc++-v3/crossconfig.m4
> index 130f47fb1d4..b3269cb88e0 100644
> --- a/libstdc++-v3/crossconfig.m4
> +++ b/libstdc++-v3/crossconfig.m4
> @@ -187,6 +187,7 @@ case "${host}" in
>      AC_CHECK_FUNCS(timespec_get)
>      AC_CHECK_FUNCS(sockatmark)
>      AC_CHECK_FUNCS(uselocale)
> +    AC_CHECK_FUNCS(secure_getenv)
>      AM_ICONV
>      ;;
>    *-mingw32*)
> diff --git a/libstdc++-v3/doc/xml/manual/configure.xml b/libstdc++-v3/doc/xml/manual/configure.xml
> index 8c26acc95a7..c6c5981968d 100644
> --- a/libstdc++-v3/doc/xml/manual/configure.xml
> +++ b/libstdc++-v3/doc/xml/manual/configure.xml
> @@ -366,6 +366,11 @@
>       </para>
>   </listitem></varlistentry>
>
> + <varlistentry><term><code>--disable-libstdcxx-hosted</code></term>
> + <listitem>
> +   <para>This is an alias for <code>--disable-hosted-libstdcxx</code>.</para>
> + </listitem></varlistentry>
> +
>  <varlistentry><term><code>--disable-libstdcxx-verbose</code></term>
>   <listitem>
>     <para>
> @@ -446,6 +451,24 @@
>      </para>
>   </listitem></varlistentry>
>
> + <varlistentry><term><code>--enable-libstdcxx-static-eh-pool</code></term>
> + <listitem>
> +    <para>Use a fixed-size static buffer for the emergency exception handling
> +      pool (see <xref linkend="intro.using.exception.alloc"/>). The default
> +      is to allocate the pool on program startup using <code>malloc</code>.
> +      With this option, a static buffer will be provided by libstdc++ instead.
> +      This does not change the library ABI.
> +    </para>
> + </listitem></varlistentry>
> +
> + <varlistentry><term><code>--with-libstdcxx-eh-pool-obj-count=NUM</code></term>
> + <listitem>
> +    <para>Set the size of the emergency exception handling pool. NUM is the
> +      number of simultaneous allocated exceptions to support.
> +      This does not change the library ABI.
> +    </para>
> + </listitem></varlistentry>
> +
>  </variablelist>
>
>  </section>
> diff --git a/libstdc++-v3/doc/xml/manual/using_exceptions.xml b/libstdc++-v3/doc/xml/manual/using_exceptions.xml
> index 32bff85549f..d17a915f237 100644
> --- a/libstdc++-v3/doc/xml/manual/using_exceptions.xml
> +++ b/libstdc++-v3/doc/xml/manual/using_exceptions.xml
> @@ -188,6 +188,54 @@ exception neutrality and exception safety.
>
>  </section>
>
> +<section xml:id="intro.using.exception.alloc" xreflabel="Memory allocation for exceptions"><info><title>Memory allocation</title></info>
> +
> +  <para>
> +    When the program throws an exception the runtime will obtain storage for
> +    a <code>__cxa_exception</code> header and the thrown object itself.
> +    Libstdc++ will try to use <code>malloc</code> to obtain storage,
> +    but provides an emergency buffer to be used if malloc fails,
> +    as described by the <link xmlns:xlink="http://www.w3.org/1999/xlink"
> +    xlink:href="https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html#imp-emergency">Itanium
> +    exception handling ABI</link>.
> +  </para>
> +
> +  <para>
> +    Contrary to the ABI, the libstdc++ emergency buffer is not always 64kB,
> +    and does not always allocate 1kB chunks. The buffer is used as a pool for
> +    variable-sized allocations, so that it doesn't waste space for smaller
> +    exception objects, such as <code>std::bad_alloc</code>.
> +    The total size of the buffer is scaled appropriately for the target,
> +    specifically it depends on <code>sizeof(void*)</code>, so that a 64-bit
> +    system uses a larger pool than a 32-bit system. This is done because for
> +    32-bit systems the exception objects (and the exception header) require
> +    less space, and core counts and thread counts are typically lower as well.
> +  </para>
> +
> +  <para>
> +    By default, libstdc++ will use <code>malloc</code> to allocate the buffer
> +    on program startup.
> +    <xref linkend="manual.intro.setup.configure"/> libstdc++ with the
> +    <code>--enable-libstdcxx-static-eh-pool</code> option will make it
> +    use a static buffer instead of using <code>malloc</code>.
> +    The default buffer size is chosen automatically, but can be overridden
> +    by configuring with <code>--with-libstdcxx-eh-pool-obj-count=NUM</code>,
> +    where NUM is the number of simultaneous allocations that should be
> +    supported. The size of the pool will be sufficient for NUM exceptions
> +    of <code>6 * sizeof(void*)</code> bytes, plus another NUM exceptions
> +    captured in <classname>std::exception_ptr</classname> and rethrown using
> +    <code>std::rethrow_exception</code>. That default size applies whether
> +    the buffer is reserved as static storage or is allocated dynamically.
> +    For a dynamic buffer, the default size can also be changed at runtime,
> +    per-process, via the <code>GLIBCXX_TUNABLES</code> environment variable.
> +  </para>
> +
> +  <para>
> +    TODO document GLIBCXX_TUNABLES
> +  </para>
> +
> +</section>
> +
>  <section xml:id="intro.using.exception.no" xreflabel="-fno-exceptions"><info><title>Doing without</title></info>
>
>    <para>
> diff --git a/libstdc++-v3/libsupc++/Makefile.am b/libstdc++-v3/libsupc++/Makefile.am
> index 65b5c1a87fd..cde9fbdb1eb 100644
> --- a/libstdc++-v3/libsupc++/Makefile.am
> +++ b/libstdc++-v3/libsupc++/Makefile.am
> @@ -139,7 +139,7 @@ atomicity.cc: ${atomicity_file}
>  # as the occasion call for it.
>  AM_CXXFLAGS = \
>         $(glibcxx_lt_pic_flag) $(glibcxx_compiler_shared_flag) \
> -       $(XTEMPLATE_FLAGS) $(FREESTANDING_FLAGS) \
> +       $(XTEMPLATE_FLAGS) $(FREESTANDING_FLAGS) $(EH_POOL_FLAGS) \
>         $(WARN_CXXFLAGS) $(OPTIMIZE_CXXFLAGS)  $(CONFIG_CXXFLAGS)
>
>  AM_MAKEFLAGS = \
> diff --git a/libstdc++-v3/libsupc++/eh_alloc.cc b/libstdc++-v3/libsupc++/eh_alloc.cc
> index 68f319869f9..32378d586ad 100644
> --- a/libstdc++-v3/libsupc++/eh_alloc.cc
> +++ b/libstdc++-v3/libsupc++/eh_alloc.cc
> @@ -25,16 +25,45 @@
>  // This is derived from the C++ ABI for IA-64.  Where we diverge
>  // for cross-architecture compatibility are noted with "@@@".
>
> -#include <bits/c++config.h>
> -#include <cstdlib>
> -#if _GLIBCXX_HOSTED
> -#include <cstring>
> -#endif
> -#include <climits>
> -#include <exception>
> +#include <exception>           // std::exception
> +#include <new>                 // std::terminate
> +#include <cstdlib>             // std::malloc, std::free, std::strtoul
> +#include <climits>             // INT_MAX
> +#include <bits/stl_function.h> // std::less
>  #include "unwind-cxx.h"
> -#include <ext/concurrence.h>
> -#include <new>
> +#if _GLIBCXX_HOSTED
> +# include <string_view>        // std::string_view
> +# include <cstring>            // std::strchr, std::memset
> +# include <ext/concurrence.h>  // __gnu_cxx::__mutex, __gnu_cxx::__scoped_lock
> +#endif
> +
> +// We use an emergency buffer used for exceptions when malloc fails.
> +// If _GLIBCXX_EH_POOL_STATIC is defined (e.g. by configure) then we use
> +// a fixed-size static buffer. Otherwise, allocate on startup using malloc.
> +//
> +// The size of the buffer is N * (S * P + R + D), where:
> +// N == The number of objects to reserve space for.
> +//      Defaults to EMERGENCY_OBJ_COUNT, defined below.
> +// S == Estimated size of exception objects to account for.
> +//      This size is in units of sizeof(void*) not bytes.
> +//      Defaults to EMERGENCY_OBJ_SIZE, defined below.
> +// P == sizeof(void*).
> +// R == sizeof(__cxa_refcounted_exception).
> +// D == sizeof(__cxa_dependent_exception).
> +//
> +// This provides space for N thrown exceptions of S words each, and an
> +// additional N dependent exceptions from std::rethrow_exception.
> +//
> +// The calculation allows values of N and S to be target-independent,
> +// as the size will be scaled by the size of basic types on the target,
> +// and space for the C++ exception header (__cxa_refcounted_exception)
> +// is added automatically.
> +//
> +// For a dynamically allocated buffer, N and S can be set from the environment.
> +// Setting N=0 will disable the emergency buffer.
> +// The GLIBCXX_TUNABLES environment variable will be checked for the following:
> +// - Tunable glibcxx.eh_pool.obj_count overrides EMERGENCY_OBJ_COUNT.
> +// - Tunable glibcxx.eh_pool.obj_size overrides EMERGENCY_OBJ_SIZE.
>
>  #if _GLIBCXX_HOSTED
>  using std::free;
> @@ -50,46 +79,61 @@ extern "C" void *memset (void *, int, std::size_t);
>
>  using namespace __cxxabiv1;
>
> -// ??? How to control these parameters.
> +// Assume that 6 * sizeof(void*) is a reasonable exception object size.
> +// Throwing very many large objects will exhaust the pool more quickly.
> +// N.B. sizeof(std::bad_alloc) == sizeof(void*)
> +// and sizeof(std::runtime_error) == 2 * sizeof(void*)
> +// and sizeof(std::system_error) == 4 * sizeof(void*).
> +#define EMERGENCY_OBJ_SIZE     6
>
> -// Guess from the size of basic types how large a buffer is reasonable.
> -// Note that the basic c++ exception header has 13 pointers and 2 ints,
> -// so on a system with PSImode pointers we're talking about 56 bytes
> -// just for overhead.
> -
> -#if INT_MAX == 32767
> -# define EMERGENCY_OBJ_SIZE    128
> -# define EMERGENCY_OBJ_COUNT   16
> -#elif !defined (_GLIBCXX_LLP64) && LONG_MAX == 2147483647
> -# define EMERGENCY_OBJ_SIZE    512
> -# define EMERGENCY_OBJ_COUNT   32
> +#ifdef __GTHREADS
> +// Assume that the number of concurrent exception objects scales with the
> +// processor word size, i.e., 16-bit systems are not likely to have hundreds
> +// or threads all simultaneously throwing on OOM conditions.
> +# define EMERGENCY_OBJ_COUNT   (8 * __SIZEOF_POINTER__)
> +# define MAX_OBJ_COUNT          (16 << __SIZEOF_POINTER__)
>  #else
> -# define EMERGENCY_OBJ_SIZE    1024
> -# define EMERGENCY_OBJ_COUNT   64
> +# define EMERGENCY_OBJ_COUNT   4
> +# define MAX_OBJ_COUNT          64
>  #endif
>
> -#ifndef __GTHREADS
> -# undef EMERGENCY_OBJ_COUNT
> -# define EMERGENCY_OBJ_COUNT   4
> +// This can be set by configure.
> +#ifdef _GLIBCXX_EH_POOL_NOBJS
> +# if _GLIBCXX_EH_POOL_NOBJS > MAX_OBJ_COUNT
> +#  warning "_GLIBCXX_EH_POOL_NOBJS value is too large; ignoring it"
> +# else
> +#  undef EMERGENCY_OBJ_COUNT
> +#  define EMERGENCY_OBJ_COUNT _GLIBCXX_EH_POOL_NOBJS
> +# endif
>  #endif
>
>  namespace __gnu_cxx
>  {
> -  void __freeres();
> +  void __freeres() noexcept;
>  }
>
>  namespace
>  {
> +  static constexpr std::size_t
> +  buffer_size_in_bytes(std::size_t obj_count, std::size_t obj_size) noexcept
> +  {
> +    // N * (S * P + R + D)
> +    constexpr std::size_t P = sizeof(void*);
> +    constexpr std::size_t R = sizeof(__cxa_refcounted_exception);
> +    constexpr std::size_t D = sizeof(__cxa_dependent_exception);
> +    return obj_count * (obj_size * P + R + D);
> +  }
> +
>    // A fixed-size heap, variable size object allocator
>    class pool
>      {
>      public:
> -      pool();
> +      pool() noexcept;
>
> -      _GLIBCXX_NODISCARD void *allocate (std::size_t);
> -      void free (void *);
> +      _GLIBCXX_NODISCARD void *allocate (std::size_t) noexcept;
> +      void free (void *) noexcept;
>
> -      bool in_pool (void *);
> +      bool in_pool (void *) const noexcept;
>
>      private:
>        struct free_entry {
> @@ -101,33 +145,87 @@ namespace
>         char data[] __attribute__((aligned));
>        };
>
> +#ifdef __GTHREADS
>        // A single mutex controlling emergency allocations.
>        __gnu_cxx::__mutex emergency_mutex;
> +      using __scoped_lock = __gnu_cxx::__scoped_lock;
> +#else
> +      int emergency_mutex = 0;
> +      struct __scoped_lock { explicit __scoped_lock(int) { } };
> +#endif
>
>        // The free-list
> -      free_entry *first_free_entry;
> +      free_entry *first_free_entry = nullptr;
>        // The arena itself - we need to keep track of these only
>        // to implement in_pool.
> -      char *arena;
> -      std::size_t arena_size;
> +#ifdef _GLIBCXX_EH_POOL_STATIC
> +      static constexpr std::size_t arena_size
> +       = buffer_size_in_bytes(EMERGENCY_OBJ_COUNT, EMERGENCY_OBJ_SIZE);
> +      alignas(void*) char arena[std::max(arena_size, sizeof(free_entry))];
> +#else
> +      char *arena = nullptr;
> +      std::size_t arena_size = 0;
> +#endif
>
> -      friend void __gnu_cxx::__freeres();
> +      friend void __gnu_cxx::__freeres() noexcept;
>      };
>
> -  pool::pool()
> +  pool::pool() noexcept
>      {
> -      // Allocate the arena - we could add a GLIBCXX_EH_ARENA_SIZE environment
> -      // to make this tunable.
> -      arena_size = (EMERGENCY_OBJ_SIZE * EMERGENCY_OBJ_COUNT
> -                   + EMERGENCY_OBJ_COUNT * sizeof (__cxa_dependent_exception));
> +#ifndef _GLIBCXX_EH_POOL_STATIC
> +      int obj_size = EMERGENCY_OBJ_SIZE;
> +      int obj_count = EMERGENCY_OBJ_COUNT;
> +
> +#if _GLIBCXX_HOSTED
> +#if _GLIBCXX_HAVE_SECURE_GETENV
> +      const char* str = ::secure_getenv("GLIBCXX_TUNABLES");
> +#else
> +      const char* str = std::getenv("GLIBCXX_TUNABLES");
> +#endif
> +      const std::string_view ns_name = "glibcxx.eh_pool";
> +      std::pair<std::string_view, int> tunables[]{
> +       {"obj_size", 0}, {"obj_count", obj_count}
> +      };
> +      while (str)
> +       {
> +         if (*str == ':')
> +           ++str;
> +
> +         if (!ns_name.compare(0, ns_name.size(), str, ns_name.size())
> +               && str[ns_name.size()] == '.')
> +         {
> +           str += ns_name.size() + 1;
> +           for (auto& t : tunables)
> +             if (!t.first.compare(0, t.first.size(), str, t.first.size())
> +                 && str[t.first.size()] == '=')
> +             {
> +               str += t.first.size() + 1;
> +               char* end;
> +               unsigned long val = strtoul(str, &end, 0);
> +               if ((*end == ':' || *end == '\0') && val <= INT_MAX)
> +                 t.second = val;
> +               str = end;
> +               break;
> +             }
> +         }
> +         str = strchr(str, ':');
> +       }
> +      obj_count = std::min(tunables[1].second, MAX_OBJ_COUNT); // Can be zero.
> +      if (tunables[0].second != 0)
> +       obj_size = tunables[0].second;
> +#endif // HOSTED
> +
> +      arena_size = buffer_size_in_bytes(obj_count, obj_size);
> +      if (arena_size == 0)
> +       return;
>        arena = (char *)malloc (arena_size);
>        if (!arena)
>         {
>           // If the allocation failed go without an emergency pool.
>           arena_size = 0;
> -         first_free_entry = NULL;
>           return;
>         }
> +#endif // STATIC
>
>        // Populate the free-list with a single entry covering the whole arena
>        first_free_entry = reinterpret_cast <free_entry *> (arena);
> @@ -136,7 +234,7 @@ namespace
>        first_free_entry->next = NULL;
>      }
>
> -  void *pool::allocate (std::size_t size)
> +  void *pool::allocate (std::size_t size) noexcept
>      {
>        __gnu_cxx::__scoped_lock sentry(emergency_mutex);
>        // We need an additional size_t member plus the padding to
> @@ -188,9 +286,9 @@ namespace
>        return &x->data;
>      }
>
> -  void pool::free (void *data)
> +  void pool::free (void *data) noexcept
>      {
> -      __gnu_cxx::__scoped_lock sentry(emergency_mutex);
> +      __scoped_lock sentry(emergency_mutex);
>        allocated_entry *e = reinterpret_cast <allocated_entry *>
>         (reinterpret_cast <char *> (data) - offsetof (allocated_entry, data));
>        std::size_t sz = e->size;
> @@ -252,11 +350,10 @@ namespace
>         }
>      }
>
> -  bool pool::in_pool (void *ptr)
> +  inline bool pool::in_pool (void *ptr) const noexcept
>      {
> -      char *p = reinterpret_cast <char *> (ptr);
> -      return (p > arena
> -             && p < arena + arena_size);
> +      std::less<const void*> less;
> +      return less(ptr, arena + arena_size) && less(arena, ptr);
>      }
>
>    pool emergency_pool;
> @@ -264,29 +361,32 @@ namespace
>
>  namespace __gnu_cxx
>  {
> +  __attribute__((cold))
>    void
> -  __freeres()
> +  __freeres() noexcept
>    {
> +#ifndef _GLIBCXX_EH_POOL_STATIC
>      if (emergency_pool.arena)
>        {
>         ::free(emergency_pool.arena);
>         emergency_pool.arena = 0;
>        }
> +#endif
>    }
>  }
>
>  extern "C" void *
> -__cxxabiv1::__cxa_allocate_exception(std::size_t thrown_size) _GLIBCXX_NOTHROW
> +__cxxabiv1::__cxa_allocate_exception(std::size_t thrown_size) noexcept
>  {
>    void *ret;
>
>    thrown_size += sizeof (__cxa_refcounted_exception);
>    ret = malloc (thrown_size);
>
> -  if (!ret)
> +  if (!ret) [[__unlikely__]]
>      ret = emergency_pool.allocate (thrown_size);
>
> -  if (!ret)
> +  if (!ret) [[__unlikely__]]
>      std::terminate ();
>
>    memset (ret, 0, sizeof (__cxa_refcounted_exception));
> @@ -296,10 +396,10 @@ __cxxabiv1::__cxa_allocate_exception(std::size_t thrown_size) _GLIBCXX_NOTHROW
>
>
>  extern "C" void
> -__cxxabiv1::__cxa_free_exception(void *vptr) _GLIBCXX_NOTHROW
> +__cxxabiv1::__cxa_free_exception(void *vptr) noexcept
>  {
>    char *ptr = (char *) vptr - sizeof (__cxa_refcounted_exception);
> -  if (emergency_pool.in_pool (ptr))
> +  if (emergency_pool.in_pool (ptr)) [[__unlikely__]]
>      emergency_pool.free (ptr);
>    else
>      free (ptr);
> @@ -307,31 +407,29 @@ __cxxabiv1::__cxa_free_exception(void *vptr) _GLIBCXX_NOTHROW
>
>
>  extern "C" __cxa_dependent_exception*
> -__cxxabiv1::__cxa_allocate_dependent_exception() _GLIBCXX_NOTHROW
> +__cxxabiv1::__cxa_allocate_dependent_exception() noexcept
>  {
> -  __cxa_dependent_exception *ret;
> +  void *ret;
>
> -  ret = static_cast<__cxa_dependent_exception*>
> -    (malloc (sizeof (__cxa_dependent_exception)));
> +  ret = malloc (sizeof (__cxa_dependent_exception));
>
> -  if (!ret)
> -    ret = static_cast <__cxa_dependent_exception*>
> -      (emergency_pool.allocate (sizeof (__cxa_dependent_exception)));
> +  if (!ret) [[__unlikely__]]
> +    ret = emergency_pool.allocate (sizeof (__cxa_dependent_exception));
>
>    if (!ret)
>      std::terminate ();
>
>    memset (ret, 0, sizeof (__cxa_dependent_exception));
>
> -  return ret;
> +  return static_cast<__cxa_dependent_exception*>(ret);
>  }
>
>
>  extern "C" void
>  __cxxabiv1::__cxa_free_dependent_exception
> -  (__cxa_dependent_exception *vptr) _GLIBCXX_NOTHROW
> +  (__cxa_dependent_exception *vptr) noexcept
>  {
> -  if (emergency_pool.in_pool (vptr))
> +  if (emergency_pool.in_pool (vptr)) [[__unlikely__]]
>      emergency_pool.free (vptr);
>    else
>      free (vptr);
>

  reply	other threads:[~2022-10-10  6:18 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-10-07 15:54 Jonathan Wakely
2022-10-10  6:17 ` Richard Biener [this message]
2022-10-10 11:17   ` Jonathan Wakely
2022-10-10 15:10     ` Jonathan Wakely
2022-10-11  6:41       ` Richard Biener
2022-10-11 11:05         ` Jonathan Wakely
2022-10-11 11:34           ` Richard Biener
2022-10-11 18:36 David Edelsohn
2022-10-11 18:57 ` Jonathan Wakely
2022-10-11 19:41   ` Jonathan Wakely

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to='CAFiYyc2mt3e1Rd=g3B2_zzaJ0DfyJDAt5GPFVQg3OqrZx=NaUw@mail.gmail.com' \
    --to=richard.guenther@gmail.com \
    --cc=gcc-patches@gcc.gnu.org \
    --cc=jwakely@redhat.com \
    --cc=libstdc++@gcc.gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).