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 
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