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