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