Home | History | Annotate | Line # | Download | only in std
      1 // <memory_resource> -*- C++ -*-
      2 
      3 // Copyright (C) 2018-2022 Free Software Foundation, Inc.
      4 //
      5 // This file is part of the GNU ISO C++ Library.  This library is free
      6 // software; you can redistribute it and/or modify it under the
      7 // terms of the GNU General Public License as published by the
      8 // Free Software Foundation; either version 3, or (at your option)
      9 // any later version.
     10 
     11 // This library is distributed in the hope that it will be useful,
     12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
     13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     14 // GNU General Public License for more details.
     15 
     16 // Under Section 7 of GPL version 3, you are granted additional
     17 // permissions described in the GCC Runtime Library Exception, version
     18 // 3.1, as published by the Free Software Foundation.
     19 
     20 // You should have received a copy of the GNU General Public License and
     21 // a copy of the GCC Runtime Library Exception along with this program;
     22 // see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
     23 // <http://www.gnu.org/licenses/>.
     24 
     25 /** @file include/memory_resource
     26  *  This is a Standard C++ Library header.
     27  */
     28 
     29 #ifndef _GLIBCXX_MEMORY_RESOURCE
     30 #define _GLIBCXX_MEMORY_RESOURCE 1
     31 
     32 #pragma GCC system_header
     33 
     34 #if __cplusplus >= 201703L
     35 
     36 #include <new>
     37 #include <vector>			// vector
     38 #include <cstddef>			// size_t, max_align_t, byte
     39 #include <shared_mutex>			// shared_mutex
     40 #include <bits/align.h>			// align
     41 #include <bits/functexcept.h>		// __throw_bad_array_new_length
     42 #include <bits/uses_allocator.h>	// allocator_arg_t, __use_alloc
     43 #include <bits/uses_allocator_args.h>	// uninitialized_construct_using_alloc
     44 #include <ext/numeric_traits.h>
     45 #include <debug/assertions.h>
     46 
     47 #if ! __cpp_lib_make_obj_using_allocator
     48 # include <bits/utility.h>		// index_sequence
     49 # include <tuple>			// tuple, forward_as_tuple
     50 #endif
     51 
     52 namespace std _GLIBCXX_VISIBILITY(default)
     53 {
     54 _GLIBCXX_BEGIN_NAMESPACE_VERSION
     55 namespace pmr
     56 {
     57 #ifdef _GLIBCXX_HAS_GTHREADS
     58   // Header and all contents are present.
     59 # define __cpp_lib_memory_resource 201603L
     60 #else
     61   // The pmr::synchronized_pool_resource type is missing.
     62 # define __cpp_lib_memory_resource 1
     63 #endif
     64 
     65   class memory_resource;
     66 
     67 #if __cplusplus == 201703L
     68   template<typename _Tp>
     69     class polymorphic_allocator;
     70 #else // C++20
     71 # define __cpp_lib_polymorphic_allocator 201902L
     72   template<typename _Tp = std::byte>
     73     class polymorphic_allocator;
     74 #endif
     75 
     76   // Global memory resources
     77 
     78   /// A pmr::memory_resource that uses `new` to allocate memory
     79   [[nodiscard, __gnu__::__returns_nonnull__, __gnu__::__const__]]
     80   memory_resource*
     81   new_delete_resource() noexcept;
     82 
     83   /// A pmr::memory_resource that always throws `bad_alloc`
     84   [[nodiscard, __gnu__::__returns_nonnull__, __gnu__::__const__]]
     85   memory_resource*
     86   null_memory_resource() noexcept;
     87 
     88   /// Replace the default memory resource pointer
     89   [[__gnu__::__returns_nonnull__]]
     90   memory_resource*
     91   set_default_resource(memory_resource* __r) noexcept;
     92 
     93   /// Get the current default memory resource pointer
     94   [[__gnu__::__returns_nonnull__]]
     95   memory_resource*
     96   get_default_resource() noexcept;
     97 
     98   // Pool resource classes
     99   struct pool_options;
    100 #ifdef _GLIBCXX_HAS_GTHREADS
    101   class synchronized_pool_resource;
    102 #endif
    103   class unsynchronized_pool_resource;
    104   class monotonic_buffer_resource;
    105 
    106   /// Class memory_resource
    107   class memory_resource
    108   {
    109     static constexpr size_t _S_max_align = alignof(max_align_t);
    110 
    111   public:
    112     memory_resource() = default;
    113     memory_resource(const memory_resource&) = default;
    114     virtual ~memory_resource(); // key function
    115 
    116     memory_resource& operator=(const memory_resource&) = default;
    117 
    118     [[nodiscard]]
    119     void*
    120     allocate(size_t __bytes, size_t __alignment = _S_max_align)
    121     __attribute__((__returns_nonnull__,__alloc_size__(2),__alloc_align__(3)))
    122     { return ::operator new(__bytes, do_allocate(__bytes, __alignment)); }
    123 
    124     void
    125     deallocate(void* __p, size_t __bytes, size_t __alignment = _S_max_align)
    126     __attribute__((__nonnull__))
    127     { return do_deallocate(__p, __bytes, __alignment); }
    128 
    129     [[nodiscard]]
    130     bool
    131     is_equal(const memory_resource& __other) const noexcept
    132     { return do_is_equal(__other); }
    133 
    134   private:
    135     virtual void*
    136     do_allocate(size_t __bytes, size_t __alignment) = 0;
    137 
    138     virtual void
    139     do_deallocate(void* __p, size_t __bytes, size_t __alignment) = 0;
    140 
    141     virtual bool
    142     do_is_equal(const memory_resource& __other) const noexcept = 0;
    143   };
    144 
    145   [[nodiscard]]
    146   inline bool
    147   operator==(const memory_resource& __a, const memory_resource& __b) noexcept
    148   { return &__a == &__b || __a.is_equal(__b); }
    149 
    150 #if __cpp_impl_three_way_comparison < 201907L
    151   [[nodiscard]]
    152   inline bool
    153   operator!=(const memory_resource& __a, const memory_resource& __b) noexcept
    154   { return !(__a == __b); }
    155 #endif
    156 
    157   // C++17 23.12.3 Class template polymorphic_allocator
    158   template<typename _Tp>
    159     class polymorphic_allocator
    160     {
    161       // _GLIBCXX_RESOLVE_LIB_DEFECTS
    162       // 2975. Missing case for pair construction in polymorphic allocators
    163       template<typename _Up>
    164 	struct __not_pair { using type = void; };
    165 
    166       template<typename _Up1, typename _Up2>
    167 	struct __not_pair<pair<_Up1, _Up2>> { };
    168 
    169     public:
    170       using value_type = _Tp;
    171 
    172       polymorphic_allocator() noexcept
    173       : _M_resource(get_default_resource())
    174       { }
    175 
    176       polymorphic_allocator(memory_resource* __r) noexcept
    177       __attribute__((__nonnull__))
    178       : _M_resource(__r)
    179       { _GLIBCXX_DEBUG_ASSERT(__r); }
    180 
    181       polymorphic_allocator(const polymorphic_allocator& __other) = default;
    182 
    183       template<typename _Up>
    184 	polymorphic_allocator(const polymorphic_allocator<_Up>& __x) noexcept
    185 	: _M_resource(__x.resource())
    186 	{ }
    187 
    188       polymorphic_allocator&
    189       operator=(const polymorphic_allocator&) = delete;
    190 
    191       [[nodiscard]]
    192       _Tp*
    193       allocate(size_t __n)
    194       __attribute__((__returns_nonnull__))
    195       {
    196 	if ((__gnu_cxx::__int_traits<size_t>::__max / sizeof(_Tp)) < __n)
    197 	  std::__throw_bad_array_new_length();
    198 	return static_cast<_Tp*>(_M_resource->allocate(__n * sizeof(_Tp),
    199 						       alignof(_Tp)));
    200       }
    201 
    202       void
    203       deallocate(_Tp* __p, size_t __n) noexcept
    204       __attribute__((__nonnull__))
    205       { _M_resource->deallocate(__p, __n * sizeof(_Tp), alignof(_Tp)); }
    206 
    207 #if __cplusplus > 201703L
    208       [[nodiscard]] void*
    209       allocate_bytes(size_t __nbytes,
    210 		     size_t __alignment = alignof(max_align_t))
    211       { return _M_resource->allocate(__nbytes, __alignment); }
    212 
    213       void
    214       deallocate_bytes(void* __p, size_t __nbytes,
    215 		       size_t __alignment = alignof(max_align_t))
    216       { _M_resource->deallocate(__p, __nbytes, __alignment); }
    217 
    218       template<typename _Up>
    219 	[[nodiscard]] _Up*
    220 	allocate_object(size_t __n = 1)
    221 	{
    222 	  if ((__gnu_cxx::__int_traits<size_t>::__max / sizeof(_Up)) < __n)
    223 	    std::__throw_bad_array_new_length();
    224 	  return static_cast<_Up*>(allocate_bytes(__n * sizeof(_Up),
    225 						  alignof(_Up)));
    226 	}
    227 
    228       template<typename _Up>
    229 	void
    230 	deallocate_object(_Up* __p, size_t __n = 1)
    231 	{ deallocate_bytes(__p, __n * sizeof(_Up), alignof(_Up)); }
    232 
    233       template<typename _Up, typename... _CtorArgs>
    234 	[[nodiscard]] _Up*
    235 	new_object(_CtorArgs&&... __ctor_args)
    236 	{
    237 	  _Up* __p = allocate_object<_Up>();
    238 	  __try
    239 	    {
    240 	      construct(__p, std::forward<_CtorArgs>(__ctor_args)...);
    241 	    }
    242 	  __catch (...)
    243 	    {
    244 	      deallocate_object(__p);
    245 	      __throw_exception_again;
    246 	    }
    247 	  return __p;
    248 	}
    249 
    250       template<typename _Up>
    251 	void
    252 	delete_object(_Up* __p)
    253 	{
    254 	  __p->~_Up();
    255 	  deallocate_object(__p);
    256 	}
    257 #endif // C++2a
    258 
    259 #if ! __cpp_lib_make_obj_using_allocator
    260       template<typename _Tp1, typename... _Args>
    261 	__attribute__((__nonnull__))
    262 	typename __not_pair<_Tp1>::type
    263 	construct(_Tp1* __p, _Args&&... __args)
    264 	{
    265 	  // _GLIBCXX_RESOLVE_LIB_DEFECTS
    266 	  // 2969. polymorphic_allocator::construct() shouldn't pass resource()
    267 	  using __use_tag
    268 	    = std::__uses_alloc_t<_Tp1, polymorphic_allocator, _Args...>;
    269 	  if constexpr (is_base_of_v<__uses_alloc0, __use_tag>)
    270 	    ::new(__p) _Tp1(std::forward<_Args>(__args)...);
    271 	  else if constexpr (is_base_of_v<__uses_alloc1_, __use_tag>)
    272 	    ::new(__p) _Tp1(allocator_arg, *this,
    273 			    std::forward<_Args>(__args)...);
    274 	  else
    275 	    ::new(__p) _Tp1(std::forward<_Args>(__args)..., *this);
    276 	}
    277 
    278       template<typename _Tp1, typename _Tp2,
    279 	       typename... _Args1, typename... _Args2>
    280 	__attribute__((__nonnull__))
    281 	void
    282 	construct(pair<_Tp1, _Tp2>* __p, piecewise_construct_t,
    283 		  tuple<_Args1...> __x, tuple<_Args2...> __y)
    284 	{
    285 	  auto __x_tag =
    286 	    __use_alloc<_Tp1, polymorphic_allocator, _Args1...>(*this);
    287 	  auto __y_tag =
    288 	    __use_alloc<_Tp2, polymorphic_allocator, _Args2...>(*this);
    289 	  index_sequence_for<_Args1...> __x_i;
    290 	  index_sequence_for<_Args2...> __y_i;
    291 
    292 	  ::new(__p) pair<_Tp1, _Tp2>(piecewise_construct,
    293 				      _S_construct_p(__x_tag, __x_i, __x),
    294 				      _S_construct_p(__y_tag, __y_i, __y));
    295 	}
    296 
    297       template<typename _Tp1, typename _Tp2>
    298 	__attribute__((__nonnull__))
    299 	void
    300 	construct(pair<_Tp1, _Tp2>* __p)
    301 	{ this->construct(__p, piecewise_construct, tuple<>(), tuple<>()); }
    302 
    303       template<typename _Tp1, typename _Tp2, typename _Up, typename _Vp>
    304 	__attribute__((__nonnull__))
    305 	void
    306 	construct(pair<_Tp1, _Tp2>* __p, _Up&& __x, _Vp&& __y)
    307 	{
    308 	  this->construct(__p, piecewise_construct,
    309 	      std::forward_as_tuple(std::forward<_Up>(__x)),
    310 	      std::forward_as_tuple(std::forward<_Vp>(__y)));
    311 	}
    312 
    313       template <typename _Tp1, typename _Tp2, typename _Up, typename _Vp>
    314 	__attribute__((__nonnull__))
    315 	void
    316 	construct(pair<_Tp1, _Tp2>* __p, const std::pair<_Up, _Vp>& __pr)
    317 	{
    318 	  this->construct(__p, piecewise_construct,
    319 	      std::forward_as_tuple(__pr.first),
    320 	      std::forward_as_tuple(__pr.second));
    321 	}
    322 
    323       template<typename _Tp1, typename _Tp2, typename _Up, typename _Vp>
    324 	__attribute__((__nonnull__))
    325 	void
    326 	construct(pair<_Tp1, _Tp2>* __p, pair<_Up, _Vp>&& __pr)
    327 	{
    328 	  this->construct(__p, piecewise_construct,
    329 	      std::forward_as_tuple(std::forward<_Up>(__pr.first)),
    330 	      std::forward_as_tuple(std::forward<_Vp>(__pr.second)));
    331 	}
    332 #else // make_obj_using_allocator
    333       template<typename _Tp1, typename... _Args>
    334 	__attribute__((__nonnull__))
    335 	void
    336 	construct(_Tp1* __p, _Args&&... __args)
    337 	{
    338 	  std::uninitialized_construct_using_allocator(__p, *this,
    339 	      std::forward<_Args>(__args)...);
    340 	}
    341 #endif
    342 
    343       template<typename _Up>
    344 	_GLIBCXX20_DEPRECATED_SUGGEST("allocator_traits::destroy")
    345 	__attribute__((__nonnull__))
    346 	void
    347 	destroy(_Up* __p)
    348 	{ __p->~_Up(); }
    349 
    350       polymorphic_allocator
    351       select_on_container_copy_construction() const noexcept
    352       { return polymorphic_allocator(); }
    353 
    354       memory_resource*
    355       resource() const noexcept
    356       __attribute__((__returns_nonnull__))
    357       { return _M_resource; }
    358 
    359       // _GLIBCXX_RESOLVE_LIB_DEFECTS
    360       // 3683. operator== for polymorphic_allocator cannot deduce template arg
    361       [[nodiscard]]
    362       friend bool
    363       operator==(const polymorphic_allocator& __a,
    364 		 const polymorphic_allocator& __b) noexcept
    365       { return *__a.resource() == *__b.resource(); }
    366 
    367 #if __cpp_impl_three_way_comparison < 201907L
    368       [[nodiscard]]
    369       friend bool
    370       operator!=(const polymorphic_allocator& __a,
    371 		 const polymorphic_allocator& __b) noexcept
    372       { return !(__a == __b); }
    373 #endif
    374 
    375     private:
    376 #if ! __cpp_lib_make_obj_using_allocator
    377       using __uses_alloc1_ = __uses_alloc1<polymorphic_allocator>;
    378       using __uses_alloc2_ = __uses_alloc2<polymorphic_allocator>;
    379 
    380       template<typename _Ind, typename... _Args>
    381 	static tuple<_Args&&...>
    382 	_S_construct_p(__uses_alloc0, _Ind, tuple<_Args...>& __t)
    383 	{ return std::move(__t); }
    384 
    385       template<size_t... _Ind, typename... _Args>
    386 	static tuple<allocator_arg_t, polymorphic_allocator, _Args&&...>
    387 	_S_construct_p(__uses_alloc1_ __ua, index_sequence<_Ind...>,
    388 		       tuple<_Args...>& __t)
    389 	{
    390 	  return {
    391 	      allocator_arg, *__ua._M_a, std::get<_Ind>(std::move(__t))...
    392 	  };
    393 	}
    394 
    395       template<size_t... _Ind, typename... _Args>
    396 	static tuple<_Args&&..., polymorphic_allocator>
    397 	_S_construct_p(__uses_alloc2_ __ua, index_sequence<_Ind...>,
    398 		       tuple<_Args...>& __t)
    399 	{ return { std::get<_Ind>(std::move(__t))..., *__ua._M_a }; }
    400 #endif
    401 
    402       memory_resource* _M_resource;
    403     };
    404 
    405   template<typename _Tp1, typename _Tp2>
    406     [[nodiscard]]
    407     inline bool
    408     operator==(const polymorphic_allocator<_Tp1>& __a,
    409 	       const polymorphic_allocator<_Tp2>& __b) noexcept
    410     { return *__a.resource() == *__b.resource(); }
    411 
    412 #if __cpp_impl_three_way_comparison < 201907L
    413   template<typename _Tp1, typename _Tp2>
    414     [[nodiscard]]
    415     inline bool
    416     operator!=(const polymorphic_allocator<_Tp1>& __a,
    417 	       const polymorphic_allocator<_Tp2>& __b) noexcept
    418     { return !(__a == __b); }
    419 #endif
    420 
    421 } // namespace pmr
    422 
    423   /// Partial specialization for std::pmr::polymorphic_allocator
    424   template<typename _Tp>
    425     struct allocator_traits<pmr::polymorphic_allocator<_Tp>>
    426     {
    427       /// The allocator type
    428       using allocator_type = pmr::polymorphic_allocator<_Tp>;
    429 
    430       /// The allocated type
    431       using value_type = _Tp;
    432 
    433       /// The allocator's pointer type.
    434       using pointer = _Tp*;
    435 
    436       /// The allocator's const pointer type.
    437       using const_pointer = const _Tp*;
    438 
    439       /// The allocator's void pointer type.
    440       using void_pointer = void*;
    441 
    442       /// The allocator's const void pointer type.
    443       using const_void_pointer = const void*;
    444 
    445       /// The allocator's difference type
    446       using difference_type = std::ptrdiff_t;
    447 
    448       /// The allocator's size type
    449       using size_type = std::size_t;
    450 
    451       /** @{
    452        * A `polymorphic_allocator` does not propagate when a
    453        * container is copied, moved, or swapped.
    454        */
    455       using propagate_on_container_copy_assignment = false_type;
    456       using propagate_on_container_move_assignment = false_type;
    457       using propagate_on_container_swap = false_type;
    458 
    459       static allocator_type
    460       select_on_container_copy_construction(const allocator_type&) noexcept
    461       { return allocator_type(); }
    462       /// @}
    463 
    464       /// Whether all instances of the allocator type compare equal.
    465       using is_always_equal = false_type;
    466 
    467       template<typename _Up>
    468 	using rebind_alloc = pmr::polymorphic_allocator<_Up>;
    469 
    470       template<typename _Up>
    471 	using rebind_traits = allocator_traits<pmr::polymorphic_allocator<_Up>>;
    472 
    473       /**
    474        *  @brief  Allocate memory.
    475        *  @param  __a  An allocator.
    476        *  @param  __n  The number of objects to allocate space for.
    477        *
    478        *  Calls `a.allocate(n)`.
    479       */
    480       [[nodiscard]] static pointer
    481       allocate(allocator_type& __a, size_type __n)
    482       { return __a.allocate(__n); }
    483 
    484       /**
    485        *  @brief  Allocate memory.
    486        *  @param  __a  An allocator.
    487        *  @param  __n  The number of objects to allocate space for.
    488        *  @return Memory of suitable size and alignment for `n` objects
    489        *          of type `value_type`.
    490        *
    491        *  The third parameter is ignored..
    492        *
    493        *  Returns `a.allocate(n)`.
    494       */
    495       [[nodiscard]] static pointer
    496       allocate(allocator_type& __a, size_type __n, const_void_pointer)
    497       { return __a.allocate(__n); }
    498 
    499       /**
    500        *  @brief  Deallocate memory.
    501        *  @param  __a  An allocator.
    502        *  @param  __p  Pointer to the memory to deallocate.
    503        *  @param  __n  The number of objects space was allocated for.
    504        *
    505        *  Calls `a.deallocate(p, n)`.
    506       */
    507       static void
    508       deallocate(allocator_type& __a, pointer __p, size_type __n)
    509       { __a.deallocate(__p, __n); }
    510 
    511       /**
    512        *  @brief  Construct an object of type `_Up`
    513        *  @param  __a  An allocator.
    514        *  @param  __p  Pointer to memory of suitable size and alignment for
    515        *	       an object of type `_Up`.
    516        *  @param  __args Constructor arguments.
    517        *
    518        *  Calls `__a.construct(__p, std::forward<_Args>(__args)...)`
    519        *  in C++11, C++14 and C++17. Changed in C++20 to call
    520        *  `std::construct_at(__p, std::forward<_Args>(__args)...)` instead.
    521       */
    522       template<typename _Up, typename... _Args>
    523 	static void
    524 	construct(allocator_type& __a, _Up* __p, _Args&&... __args)
    525 	{ __a.construct(__p, std::forward<_Args>(__args)...); }
    526 
    527       /**
    528        *  @brief  Destroy an object of type `_Up`
    529        *  @param  __a  An allocator.
    530        *  @param  __p  Pointer to the object to destroy
    531        *
    532        *  Calls `p->_Up()`.
    533       */
    534       template<typename _Up>
    535 	static _GLIBCXX20_CONSTEXPR void
    536 	destroy(allocator_type&, _Up* __p)
    537 	noexcept(is_nothrow_destructible<_Up>::value)
    538 	{ __p->~_Up(); }
    539 
    540       /**
    541        *  @brief  The maximum supported allocation size
    542        *  @return `numeric_limits<size_t>::max() / sizeof(value_type)`
    543       */
    544       static _GLIBCXX20_CONSTEXPR size_type
    545       max_size(const allocator_type&) noexcept
    546       { return size_t(-1) / sizeof(value_type); }
    547     };
    548 
    549 namespace pmr
    550 {
    551   /// Parameters for tuning a pool resource's behaviour.
    552   struct pool_options
    553   {
    554     /** @brief Upper limit on number of blocks in a chunk.
    555      *
    556      * A lower value prevents allocating huge chunks that could remain mostly
    557      * unused, but means pools will need to replenished more frequently.
    558      */
    559     size_t max_blocks_per_chunk = 0;
    560 
    561     /* @brief Largest block size (in bytes) that should be served from pools.
    562      *
    563      * Larger allocations will be served directly by the upstream resource,
    564      * not from one of the pools managed by the pool resource.
    565      */
    566     size_t largest_required_pool_block = 0;
    567   };
    568 
    569   // Common implementation details for un-/synchronized pool resources.
    570   class __pool_resource
    571   {
    572     friend class synchronized_pool_resource;
    573     friend class unsynchronized_pool_resource;
    574 
    575     __pool_resource(const pool_options& __opts, memory_resource* __upstream);
    576 
    577     ~__pool_resource();
    578 
    579     __pool_resource(const __pool_resource&) = delete;
    580     __pool_resource& operator=(const __pool_resource&) = delete;
    581 
    582     // Allocate a large unpooled block.
    583     void*
    584     allocate(size_t __bytes, size_t __alignment);
    585 
    586     // Deallocate a large unpooled block.
    587     void
    588     deallocate(void* __p, size_t __bytes, size_t __alignment);
    589 
    590 
    591     // Deallocate unpooled memory.
    592     void release() noexcept;
    593 
    594     memory_resource* resource() const noexcept
    595     { return _M_unpooled.get_allocator().resource(); }
    596 
    597     struct _Pool;
    598 
    599     _Pool* _M_alloc_pools();
    600 
    601     const pool_options _M_opts;
    602 
    603     struct _BigBlock;
    604     // Collection of blocks too big for any pool, sorted by address.
    605     // This also stores the only copy of the upstream memory resource pointer.
    606     _GLIBCXX_STD_C::pmr::vector<_BigBlock> _M_unpooled;
    607 
    608     const int _M_npools;
    609   };
    610 
    611 #ifdef _GLIBCXX_HAS_GTHREADS
    612   /// A thread-safe memory resource that manages pools of fixed-size blocks.
    613   class synchronized_pool_resource : public memory_resource
    614   {
    615   public:
    616     synchronized_pool_resource(const pool_options& __opts,
    617 				 memory_resource* __upstream)
    618     __attribute__((__nonnull__));
    619 
    620     synchronized_pool_resource()
    621     : synchronized_pool_resource(pool_options(), get_default_resource())
    622     { }
    623 
    624     explicit
    625     synchronized_pool_resource(memory_resource* __upstream)
    626     __attribute__((__nonnull__))
    627     : synchronized_pool_resource(pool_options(), __upstream)
    628     { }
    629 
    630     explicit
    631     synchronized_pool_resource(const pool_options& __opts)
    632     : synchronized_pool_resource(__opts, get_default_resource()) { }
    633 
    634     synchronized_pool_resource(const synchronized_pool_resource&) = delete;
    635 
    636     virtual ~synchronized_pool_resource();
    637 
    638     synchronized_pool_resource&
    639     operator=(const synchronized_pool_resource&) = delete;
    640 
    641     void release();
    642 
    643     memory_resource*
    644     upstream_resource() const noexcept
    645     __attribute__((__returns_nonnull__))
    646     { return _M_impl.resource(); }
    647 
    648     pool_options options() const noexcept { return _M_impl._M_opts; }
    649 
    650   protected:
    651     void*
    652     do_allocate(size_t __bytes, size_t __alignment) override;
    653 
    654     void
    655     do_deallocate(void* __p, size_t __bytes, size_t __alignment) override;
    656 
    657     bool
    658     do_is_equal(const memory_resource& __other) const noexcept override
    659     { return this == &__other; }
    660 
    661   public:
    662     // Thread-specific pools (only public for access by implementation details)
    663     struct _TPools;
    664 
    665   private:
    666     _TPools* _M_alloc_tpools(lock_guard<shared_mutex>&);
    667     _TPools* _M_alloc_shared_tpools(lock_guard<shared_mutex>&);
    668     auto _M_thread_specific_pools() noexcept;
    669 
    670     __pool_resource _M_impl;
    671     __gthread_key_t _M_key;
    672     // Linked list of thread-specific pools. All threads share _M_tpools[0].
    673     _TPools* _M_tpools = nullptr;
    674     mutable shared_mutex _M_mx;
    675   };
    676 #endif
    677 
    678   /// A non-thread-safe memory resource that manages pools of fixed-size blocks.
    679   class unsynchronized_pool_resource : public memory_resource
    680   {
    681   public:
    682     [[__gnu__::__nonnull__]]
    683     unsynchronized_pool_resource(const pool_options& __opts,
    684 				 memory_resource* __upstream);
    685 
    686     unsynchronized_pool_resource()
    687     : unsynchronized_pool_resource(pool_options(), get_default_resource())
    688     { }
    689 
    690     [[__gnu__::__nonnull__]]
    691     explicit
    692     unsynchronized_pool_resource(memory_resource* __upstream)
    693     : unsynchronized_pool_resource(pool_options(), __upstream)
    694     { }
    695 
    696     explicit
    697     unsynchronized_pool_resource(const pool_options& __opts)
    698     : unsynchronized_pool_resource(__opts, get_default_resource()) { }
    699 
    700     unsynchronized_pool_resource(const unsynchronized_pool_resource&) = delete;
    701 
    702     virtual ~unsynchronized_pool_resource();
    703 
    704     unsynchronized_pool_resource&
    705     operator=(const unsynchronized_pool_resource&) = delete;
    706 
    707     void release();
    708 
    709     [[__gnu__::__returns_nonnull__]]
    710     memory_resource*
    711     upstream_resource() const noexcept
    712     { return _M_impl.resource(); }
    713 
    714     pool_options options() const noexcept { return _M_impl._M_opts; }
    715 
    716   protected:
    717     void*
    718     do_allocate(size_t __bytes, size_t __alignment) override;
    719 
    720     void
    721     do_deallocate(void* __p, size_t __bytes, size_t __alignment) override;
    722 
    723     bool
    724     do_is_equal(const memory_resource& __other) const noexcept override
    725     { return this == &__other; }
    726 
    727   private:
    728     using _Pool = __pool_resource::_Pool;
    729 
    730     auto _M_find_pool(size_t) noexcept;
    731 
    732     __pool_resource _M_impl;
    733     _Pool* _M_pools = nullptr;
    734   };
    735 
    736   class monotonic_buffer_resource : public memory_resource
    737   {
    738   public:
    739     explicit
    740     monotonic_buffer_resource(memory_resource* __upstream) noexcept
    741     __attribute__((__nonnull__))
    742     : _M_upstream(__upstream)
    743     { _GLIBCXX_DEBUG_ASSERT(__upstream != nullptr); }
    744 
    745     monotonic_buffer_resource(size_t __initial_size,
    746 			      memory_resource* __upstream) noexcept
    747     __attribute__((__nonnull__))
    748     : _M_next_bufsiz(__initial_size),
    749       _M_upstream(__upstream)
    750     {
    751       _GLIBCXX_DEBUG_ASSERT(__upstream != nullptr);
    752       _GLIBCXX_DEBUG_ASSERT(__initial_size > 0);
    753     }
    754 
    755     monotonic_buffer_resource(void* __buffer, size_t __buffer_size,
    756 			      memory_resource* __upstream) noexcept
    757     __attribute__((__nonnull__(4)))
    758     : _M_current_buf(__buffer), _M_avail(__buffer_size),
    759       _M_next_bufsiz(_S_next_bufsize(__buffer_size)),
    760       _M_upstream(__upstream),
    761       _M_orig_buf(__buffer), _M_orig_size(__buffer_size)
    762     {
    763       _GLIBCXX_DEBUG_ASSERT(__upstream != nullptr);
    764       _GLIBCXX_DEBUG_ASSERT(__buffer != nullptr || __buffer_size == 0);
    765     }
    766 
    767     monotonic_buffer_resource() noexcept
    768     : monotonic_buffer_resource(get_default_resource())
    769     { }
    770 
    771     explicit
    772     monotonic_buffer_resource(size_t __initial_size) noexcept
    773     : monotonic_buffer_resource(__initial_size, get_default_resource())
    774     { }
    775 
    776     monotonic_buffer_resource(void* __buffer, size_t __buffer_size) noexcept
    777     : monotonic_buffer_resource(__buffer, __buffer_size, get_default_resource())
    778     { }
    779 
    780     monotonic_buffer_resource(const monotonic_buffer_resource&) = delete;
    781 
    782     virtual ~monotonic_buffer_resource(); // key function
    783 
    784     monotonic_buffer_resource&
    785     operator=(const monotonic_buffer_resource&) = delete;
    786 
    787     void
    788     release() noexcept
    789     {
    790       if (_M_head)
    791 	_M_release_buffers();
    792 
    793       // reset to initial state at contruction:
    794       if ((_M_current_buf = _M_orig_buf))
    795 	{
    796 	  _M_avail = _M_orig_size;
    797 	  _M_next_bufsiz = _S_next_bufsize(_M_orig_size);
    798 	}
    799       else
    800 	{
    801 	  _M_avail = 0;
    802 	  _M_next_bufsiz = _M_orig_size;
    803 	}
    804     }
    805 
    806     memory_resource*
    807     upstream_resource() const noexcept
    808     __attribute__((__returns_nonnull__))
    809     { return _M_upstream; }
    810 
    811   protected:
    812     void*
    813     do_allocate(size_t __bytes, size_t __alignment) override
    814     {
    815       if (__builtin_expect(__bytes == 0, false))
    816 	__bytes = 1; // Ensures we don't return the same pointer twice.
    817 
    818       void* __p = std::align(__alignment, __bytes, _M_current_buf, _M_avail);
    819       if (__builtin_expect(__p == nullptr, false))
    820 	{
    821 	  _M_new_buffer(__bytes, __alignment);
    822 	  __p = _M_current_buf;
    823 	}
    824       _M_current_buf = (char*)_M_current_buf + __bytes;
    825       _M_avail -= __bytes;
    826       return __p;
    827     }
    828 
    829     void
    830     do_deallocate(void*, size_t, size_t) override
    831     { }
    832 
    833     bool
    834     do_is_equal(const memory_resource& __other) const noexcept override
    835     { return this == &__other; }
    836 
    837   private:
    838     // Update _M_current_buf and _M_avail to refer to a new buffer with
    839     // at least the specified size and alignment, allocated from upstream.
    840     void
    841     _M_new_buffer(size_t __bytes, size_t __alignment);
    842 
    843     // Deallocate all buffers obtained from upstream.
    844     void
    845     _M_release_buffers() noexcept;
    846 
    847     static size_t
    848     _S_next_bufsize(size_t __buffer_size) noexcept
    849     {
    850       if (__builtin_expect(__buffer_size == 0, false))
    851 	__buffer_size = 1;
    852       return __buffer_size * _S_growth_factor;
    853     }
    854 
    855     static constexpr size_t _S_init_bufsize = 128 * sizeof(void*);
    856     static constexpr float _S_growth_factor = 1.5;
    857 
    858     void*	_M_current_buf = nullptr;
    859     size_t	_M_avail = 0;
    860     size_t	_M_next_bufsiz = _S_init_bufsize;
    861 
    862     // Initial values set at construction and reused by release():
    863     memory_resource* const	_M_upstream;
    864     void* const			_M_orig_buf = nullptr;
    865     size_t const		_M_orig_size = _M_next_bufsiz;
    866 
    867     class _Chunk;
    868     _Chunk* _M_head = nullptr;
    869   };
    870 
    871 } // namespace pmr
    872 _GLIBCXX_END_NAMESPACE_VERSION
    873 } // namespace std
    874 
    875 #endif // C++17
    876 #endif // _GLIBCXX_MEMORY_RESOURCE
    877