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