xref: /netbsd-src/external/gpl3/gcc/dist/libstdc++-v3/include/std/memory_resource (revision b1e838363e3c6fc78a55519254d99869742dd33c)
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
52namespace std _GLIBCXX_VISIBILITY(default)
53{
54_GLIBCXX_BEGIN_NAMESPACE_VERSION
55namespace 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
549namespace 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