libstdc++
memory_resource
Go to the documentation of this file.
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  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  // _GLIBCXX_RESOLVE_LIB_DEFECTS
342  // 3683. operator== for polymorphic_allocator cannot deduce template arg
343  [[nodiscard]]
344  friend bool
345  operator==(const polymorphic_allocator& __a,
346  const polymorphic_allocator& __b) noexcept
347  { return *__a.resource() == *__b.resource(); }
348 
349 #if __cpp_impl_three_way_comparison < 201907L
350  [[nodiscard]]
351  friend bool
352  operator!=(const polymorphic_allocator& __a,
353  const polymorphic_allocator& __b) noexcept
354  { return !(__a == __b); }
355 #endif
356 
357  private:
358 #if ! __cpp_lib_make_obj_using_allocator
359  using __uses_alloc1_ = __uses_alloc1<polymorphic_allocator>;
360  using __uses_alloc2_ = __uses_alloc2<polymorphic_allocator>;
361 
362  template<typename _Ind, typename... _Args>
363  static tuple<_Args&&...>
364  _S_construct_p(__uses_alloc0, _Ind, tuple<_Args...>& __t)
365  { return std::move(__t); }
366 
367  template<size_t... _Ind, typename... _Args>
368  static tuple<allocator_arg_t, polymorphic_allocator, _Args&&...>
369  _S_construct_p(__uses_alloc1_ __ua, index_sequence<_Ind...>,
370  tuple<_Args...>& __t)
371  {
372  return {
373  allocator_arg, *__ua._M_a, std::get<_Ind>(std::move(__t))...
374  };
375  }
376 
377  template<size_t... _Ind, typename... _Args>
378  static tuple<_Args&&..., polymorphic_allocator>
379  _S_construct_p(__uses_alloc2_ __ua, index_sequence<_Ind...>,
380  tuple<_Args...>& __t)
381  { return { std::get<_Ind>(std::move(__t))..., *__ua._M_a }; }
382 #endif
383 
384  memory_resource* _M_resource;
385  };
386 
387  template<typename _Tp1, typename _Tp2>
388  inline bool
389  operator==(const polymorphic_allocator<_Tp1>& __a,
390  const polymorphic_allocator<_Tp2>& __b) noexcept
391  { return *__a.resource() == *__b.resource(); }
392 
393 #if __cpp_impl_three_way_comparison < 201907L
394  template<typename _Tp1, typename _Tp2>
395  inline bool
396  operator!=(const polymorphic_allocator<_Tp1>& __a,
397  const polymorphic_allocator<_Tp2>& __b) noexcept
398  { return !(__a == __b); }
399 #endif
400 
401 } // namespace pmr
402 
403  /// Partial specialization for std::pmr::polymorphic_allocator
404  template<typename _Tp>
405  struct allocator_traits<pmr::polymorphic_allocator<_Tp>>
406  {
407  /// The allocator type
408  using allocator_type = pmr::polymorphic_allocator<_Tp>;
409 
410  /// The allocated type
411  using value_type = _Tp;
412 
413  /// The allocator's pointer type.
414  using pointer = _Tp*;
415 
416  /// The allocator's const pointer type.
417  using const_pointer = const _Tp*;
418 
419  /// The allocator's void pointer type.
420  using void_pointer = void*;
421 
422  /// The allocator's const void pointer type.
423  using const_void_pointer = const void*;
424 
425  /// The allocator's difference type
426  using difference_type = std::ptrdiff_t;
427 
428  /// The allocator's size type
429  using size_type = std::size_t;
430 
431  /** @{
432  * A `polymorphic_allocator` does not propagate when a
433  * container is copied, moved, or swapped.
434  */
435  using propagate_on_container_copy_assignment = false_type;
436  using propagate_on_container_move_assignment = false_type;
437  using propagate_on_container_swap = false_type;
438 
439  static allocator_type
440  select_on_container_copy_construction(const allocator_type&) noexcept
441  { return allocator_type(); }
442  /// @}
443 
444  /// Whether all instances of the allocator type compare equal.
445  using is_always_equal = false_type;
446 
447  template<typename _Up>
448  using rebind_alloc = pmr::polymorphic_allocator<_Up>;
449 
450  template<typename _Up>
451  using rebind_traits = allocator_traits<pmr::polymorphic_allocator<_Up>>;
452 
453  /**
454  * @brief Allocate memory.
455  * @param __a An allocator.
456  * @param __n The number of objects to allocate space for.
457  *
458  * Calls `a.allocate(n)`.
459  */
460  [[nodiscard]] static pointer
461  allocate(allocator_type& __a, size_type __n)
462  { return __a.allocate(__n); }
463 
464  /**
465  * @brief Allocate memory.
466  * @param __a An allocator.
467  * @param __n The number of objects to allocate space for.
468  * @return Memory of suitable size and alignment for `n` objects
469  * of type `value_type`.
470  *
471  * The third parameter is ignored..
472  *
473  * Returns `a.allocate(n)`.
474  */
475  [[nodiscard]] static pointer
476  allocate(allocator_type& __a, size_type __n, const_void_pointer)
477  { return __a.allocate(__n); }
478 
479  /**
480  * @brief Deallocate memory.
481  * @param __a An allocator.
482  * @param __p Pointer to the memory to deallocate.
483  * @param __n The number of objects space was allocated for.
484  *
485  * Calls `a.deallocate(p, n)`.
486  */
487  static void
488  deallocate(allocator_type& __a, pointer __p, size_type __n)
489  { __a.deallocate(__p, __n); }
490 
491  /**
492  * @brief Construct an object of type `_Up`
493  * @param __a An allocator.
494  * @param __p Pointer to memory of suitable size and alignment for
495  * an object of type `_Up`.
496  * @param __args Constructor arguments.
497  *
498  * Calls `__a.construct(__p, std::forward<_Args>(__args)...)`
499  * in C++11, C++14 and C++17. Changed in C++20 to call
500  * `std::construct_at(__p, std::forward<_Args>(__args)...)` instead.
501  */
502  template<typename _Up, typename... _Args>
503  static void
504  construct(allocator_type& __a, _Up* __p, _Args&&... __args)
505  { __a.construct(__p, std::forward<_Args>(__args)...); }
506 
507  /**
508  * @brief Destroy an object of type `_Up`
509  * @param __a An allocator.
510  * @param __p Pointer to the object to destroy
511  *
512  * Calls `p->_Up()`.
513  */
514  template<typename _Up>
515  static _GLIBCXX20_CONSTEXPR void
516  destroy(allocator_type&, _Up* __p)
517  noexcept(is_nothrow_destructible<_Up>::value)
518  { __p->~_Up(); }
519 
520  /**
521  * @brief The maximum supported allocation size
522  * @return `numeric_limits<size_t>::max() / sizeof(value_type)`
523  */
524  static _GLIBCXX20_CONSTEXPR size_type
525  max_size(const allocator_type&) noexcept
526  { return size_t(-1) / sizeof(value_type); }
527  };
528 
529 namespace pmr
530 {
531  /// Parameters for tuning a pool resource's behaviour.
532  struct pool_options
533  {
534  /** @brief Upper limit on number of blocks in a chunk.
535  *
536  * A lower value prevents allocating huge chunks that could remain mostly
537  * unused, but means pools will need to replenished more frequently.
538  */
539  size_t max_blocks_per_chunk = 0;
540 
541  /* @brief Largest block size (in bytes) that should be served from pools.
542  *
543  * Larger allocations will be served directly by the upstream resource,
544  * not from one of the pools managed by the pool resource.
545  */
546  size_t largest_required_pool_block = 0;
547  };
548 
549  // Common implementation details for un-/synchronized pool resources.
550  class __pool_resource
551  {
552  friend class synchronized_pool_resource;
553  friend class unsynchronized_pool_resource;
554 
555  __pool_resource(const pool_options& __opts, memory_resource* __upstream);
556 
557  ~__pool_resource();
558 
559  __pool_resource(const __pool_resource&) = delete;
560  __pool_resource& operator=(const __pool_resource&) = delete;
561 
562  // Allocate a large unpooled block.
563  void*
564  allocate(size_t __bytes, size_t __alignment);
565 
566  // Deallocate a large unpooled block.
567  void
568  deallocate(void* __p, size_t __bytes, size_t __alignment);
569 
570 
571  // Deallocate unpooled memory.
572  void release() noexcept;
573 
574  memory_resource* resource() const noexcept
575  { return _M_unpooled.get_allocator().resource(); }
576 
577  struct _Pool;
578 
579  _Pool* _M_alloc_pools();
580 
581  const pool_options _M_opts;
582 
583  struct _BigBlock;
584  // Collection of blocks too big for any pool, sorted by address.
585  // This also stores the only copy of the upstream memory resource pointer.
586  _GLIBCXX_STD_C::pmr::vector<_BigBlock> _M_unpooled;
587 
588  const int _M_npools;
589  };
590 
591 #ifdef _GLIBCXX_HAS_GTHREADS
592  /// A thread-safe memory resource that manages pools of fixed-size blocks.
593  class synchronized_pool_resource : public memory_resource
594  {
595  public:
596  synchronized_pool_resource(const pool_options& __opts,
597  memory_resource* __upstream)
598  __attribute__((__nonnull__));
599 
600  synchronized_pool_resource()
601  : synchronized_pool_resource(pool_options(), get_default_resource())
602  { }
603 
604  explicit
605  synchronized_pool_resource(memory_resource* __upstream)
606  __attribute__((__nonnull__))
607  : synchronized_pool_resource(pool_options(), __upstream)
608  { }
609 
610  explicit
611  synchronized_pool_resource(const pool_options& __opts)
612  : synchronized_pool_resource(__opts, get_default_resource()) { }
613 
614  synchronized_pool_resource(const synchronized_pool_resource&) = delete;
615 
616  virtual ~synchronized_pool_resource();
617 
618  synchronized_pool_resource&
619  operator=(const synchronized_pool_resource&) = delete;
620 
621  void release();
622 
623  memory_resource*
624  upstream_resource() const noexcept
625  __attribute__((__returns_nonnull__))
626  { return _M_impl.resource(); }
627 
628  pool_options options() const noexcept { return _M_impl._M_opts; }
629 
630  protected:
631  void*
632  do_allocate(size_t __bytes, size_t __alignment) override;
633 
634  void
635  do_deallocate(void* __p, size_t __bytes, size_t __alignment) override;
636 
637  bool
638  do_is_equal(const memory_resource& __other) const noexcept override
639  { return this == &__other; }
640 
641  public:
642  // Thread-specific pools (only public for access by implementation details)
643  struct _TPools;
644 
645  private:
646  _TPools* _M_alloc_tpools(lock_guard<shared_mutex>&);
647  _TPools* _M_alloc_shared_tpools(lock_guard<shared_mutex>&);
648  auto _M_thread_specific_pools() noexcept;
649 
650  __pool_resource _M_impl;
651  __gthread_key_t _M_key;
652  // Linked list of thread-specific pools. All threads share _M_tpools[0].
653  _TPools* _M_tpools = nullptr;
654  mutable shared_mutex _M_mx;
655  };
656 #endif
657 
658  /// A non-thread-safe memory resource that manages pools of fixed-size blocks.
659  class unsynchronized_pool_resource : public memory_resource
660  {
661  public:
662  [[__gnu__::__nonnull__]]
663  unsynchronized_pool_resource(const pool_options& __opts,
664  memory_resource* __upstream);
665 
666  unsynchronized_pool_resource()
667  : unsynchronized_pool_resource(pool_options(), get_default_resource())
668  { }
669 
670  [[__gnu__::__nonnull__]]
671  explicit
672  unsynchronized_pool_resource(memory_resource* __upstream)
673  : unsynchronized_pool_resource(pool_options(), __upstream)
674  { }
675 
676  explicit
677  unsynchronized_pool_resource(const pool_options& __opts)
678  : unsynchronized_pool_resource(__opts, get_default_resource()) { }
679 
680  unsynchronized_pool_resource(const unsynchronized_pool_resource&) = delete;
681 
682  virtual ~unsynchronized_pool_resource();
683 
684  unsynchronized_pool_resource&
685  operator=(const unsynchronized_pool_resource&) = delete;
686 
687  void release();
688 
689  [[__gnu__::__returns_nonnull__]]
690  memory_resource*
691  upstream_resource() const noexcept
692  { return _M_impl.resource(); }
693 
694  pool_options options() const noexcept { return _M_impl._M_opts; }
695 
696  protected:
697  void*
698  do_allocate(size_t __bytes, size_t __alignment) override;
699 
700  void
701  do_deallocate(void* __p, size_t __bytes, size_t __alignment) override;
702 
703  bool
704  do_is_equal(const memory_resource& __other) const noexcept override
705  { return this == &__other; }
706 
707  private:
708  using _Pool = __pool_resource::_Pool;
709 
710  auto _M_find_pool(size_t) noexcept;
711 
712  __pool_resource _M_impl;
713  _Pool* _M_pools = nullptr;
714  };
715 
716  class monotonic_buffer_resource : public memory_resource
717  {
718  public:
719  explicit
720  monotonic_buffer_resource(memory_resource* __upstream) noexcept
721  __attribute__((__nonnull__))
722  : _M_upstream(__upstream)
723  { _GLIBCXX_DEBUG_ASSERT(__upstream != nullptr); }
724 
725  monotonic_buffer_resource(size_t __initial_size,
726  memory_resource* __upstream) noexcept
727  __attribute__((__nonnull__))
728  : _M_next_bufsiz(__initial_size),
729  _M_upstream(__upstream)
730  {
731  _GLIBCXX_DEBUG_ASSERT(__upstream != nullptr);
732  _GLIBCXX_DEBUG_ASSERT(__initial_size > 0);
733  }
734 
735  monotonic_buffer_resource(void* __buffer, size_t __buffer_size,
736  memory_resource* __upstream) noexcept
737  __attribute__((__nonnull__(4)))
738  : _M_current_buf(__buffer), _M_avail(__buffer_size),
739  _M_next_bufsiz(_S_next_bufsize(__buffer_size)),
740  _M_upstream(__upstream),
741  _M_orig_buf(__buffer), _M_orig_size(__buffer_size)
742  {
743  _GLIBCXX_DEBUG_ASSERT(__upstream != nullptr);
744  _GLIBCXX_DEBUG_ASSERT(__buffer != nullptr || __buffer_size == 0);
745  }
746 
747  monotonic_buffer_resource() noexcept
748  : monotonic_buffer_resource(get_default_resource())
749  { }
750 
751  explicit
752  monotonic_buffer_resource(size_t __initial_size) noexcept
753  : monotonic_buffer_resource(__initial_size, get_default_resource())
754  { }
755 
756  monotonic_buffer_resource(void* __buffer, size_t __buffer_size) noexcept
757  : monotonic_buffer_resource(__buffer, __buffer_size, get_default_resource())
758  { }
759 
760  monotonic_buffer_resource(const monotonic_buffer_resource&) = delete;
761 
762  virtual ~monotonic_buffer_resource(); // key function
763 
764  monotonic_buffer_resource&
765  operator=(const monotonic_buffer_resource&) = delete;
766 
767  void
768  release() noexcept
769  {
770  if (_M_head)
771  _M_release_buffers();
772 
773  // reset to initial state at contruction:
774  if ((_M_current_buf = _M_orig_buf))
775  {
776  _M_avail = _M_orig_size;
777  _M_next_bufsiz = _S_next_bufsize(_M_orig_size);
778  }
779  else
780  {
781  _M_avail = 0;
782  _M_next_bufsiz = _M_orig_size;
783  }
784  }
785 
786  memory_resource*
787  upstream_resource() const noexcept
788  __attribute__((__returns_nonnull__))
789  { return _M_upstream; }
790 
791  protected:
792  void*
793  do_allocate(size_t __bytes, size_t __alignment) override
794  {
795  if (__builtin_expect(__bytes == 0, false))
796  __bytes = 1; // Ensures we don't return the same pointer twice.
797 
798  void* __p = std::align(__alignment, __bytes, _M_current_buf, _M_avail);
799  if (__builtin_expect(__p == nullptr, false))
800  {
801  _M_new_buffer(__bytes, __alignment);
802  __p = _M_current_buf;
803  }
804  _M_current_buf = (char*)_M_current_buf + __bytes;
805  _M_avail -= __bytes;
806  return __p;
807  }
808 
809  void
810  do_deallocate(void*, size_t, size_t) override
811  { }
812 
813  bool
814  do_is_equal(const memory_resource& __other) const noexcept override
815  { return this == &__other; }
816 
817  private:
818  // Update _M_current_buf and _M_avail to refer to a new buffer with
819  // at least the specified size and alignment, allocated from upstream.
820  void
821  _M_new_buffer(size_t __bytes, size_t __alignment);
822 
823  // Deallocate all buffers obtained from upstream.
824  void
825  _M_release_buffers() noexcept;
826 
827  static size_t
828  _S_next_bufsize(size_t __buffer_size) noexcept
829  {
830  if (__builtin_expect(__buffer_size == 0, false))
831  __buffer_size = 1;
832  return __buffer_size * _S_growth_factor;
833  }
834 
835  static constexpr size_t _S_init_bufsize = 128 * sizeof(void*);
836  static constexpr float _S_growth_factor = 1.5;
837 
838  void* _M_current_buf = nullptr;
839  size_t _M_avail = 0;
840  size_t _M_next_bufsiz = _S_init_bufsize;
841 
842  // Initial values set at construction and reused by release():
843  memory_resource* const _M_upstream;
844  void* const _M_orig_buf = nullptr;
845  size_t const _M_orig_size = _M_next_bufsiz;
846 
847  class _Chunk;
848  _Chunk* _M_head = nullptr;
849  };
850 
851 } // namespace pmr
852 _GLIBCXX_END_NAMESPACE_VERSION
853 } // namespace std
854 
855 #endif // C++17
856 #endif // _GLIBCXX_MEMORY_RESOURCE