1// <memory_resource> -*- C++ -*- 2 3// Copyright (C) 2018-2019 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 <limits> // numeric_limits 37#include <memory> // align, allocator_arg_t, __uses_alloc 38#include <utility> // pair, index_sequence 39#include <vector> // vector 40#include <cstddef> // size_t, max_align_t, byte 41#include <shared_mutex> // shared_mutex 42#include <bits/functexcept.h> 43#include <debug/assertions.h> 44 45namespace std _GLIBCXX_VISIBILITY(default) 46{ 47_GLIBCXX_BEGIN_NAMESPACE_VERSION 48namespace pmr 49{ 50#ifdef _GLIBCXX_HAS_GTHREADS 51 // Header and all contents are present. 52# define __cpp_lib_memory_resource 201603 53#else 54 // The pmr::synchronized_pool_resource type is missing. 55# define __cpp_lib_memory_resource 1 56#endif 57 58 class memory_resource; 59 60#if __cplusplus == 201703L 61 template<typename _Tp> 62 class polymorphic_allocator; 63#else // C++20 64# define __cpp_lib_polymorphic_allocator 201902L 65 template<typename _Tp = std::byte> 66 class polymorphic_allocator; 67#endif 68 69 // Global memory resources 70 memory_resource* new_delete_resource() noexcept; 71 memory_resource* null_memory_resource() noexcept; 72 memory_resource* set_default_resource(memory_resource* __r) noexcept; 73 memory_resource* get_default_resource() noexcept 74 __attribute__((__returns_nonnull__)); 75 76 // Pool resource classes 77 struct pool_options; 78#ifdef _GLIBCXX_HAS_GTHREADS 79 class synchronized_pool_resource; 80#endif 81 class unsynchronized_pool_resource; 82 class monotonic_buffer_resource; 83 84 /// Class memory_resource 85 class memory_resource 86 { 87 static constexpr size_t _S_max_align = alignof(max_align_t); 88 89 public: 90 memory_resource() = default; 91 memory_resource(const memory_resource&) = default; 92 virtual ~memory_resource(); // key function 93 94 memory_resource& operator=(const memory_resource&) = default; 95 96 [[nodiscard]] 97 void* 98 allocate(size_t __bytes, size_t __alignment = _S_max_align) 99 __attribute__((__returns_nonnull__,__alloc_size__(2),__alloc_align__(3))) 100 { return do_allocate(__bytes, __alignment); } 101 102 void 103 deallocate(void* __p, size_t __bytes, size_t __alignment = _S_max_align) 104 __attribute__((__nonnull__)) 105 { return do_deallocate(__p, __bytes, __alignment); } 106 107 bool 108 is_equal(const memory_resource& __other) const noexcept 109 { return do_is_equal(__other); } 110 111 private: 112 virtual void* 113 do_allocate(size_t __bytes, size_t __alignment) = 0; 114 115 virtual void 116 do_deallocate(void* __p, size_t __bytes, size_t __alignment) = 0; 117 118 virtual bool 119 do_is_equal(const memory_resource& __other) const noexcept = 0; 120 }; 121 122 inline bool 123 operator==(const memory_resource& __a, const memory_resource& __b) noexcept 124 { return &__a == &__b || __a.is_equal(__b); } 125 126 inline bool 127 operator!=(const memory_resource& __a, const memory_resource& __b) noexcept 128 { return !(__a == __b); } 129 130 131 // C++17 23.12.3 Class template polymorphic_allocator 132 template<typename _Tp> 133 class polymorphic_allocator 134 { 135 // _GLIBCXX_RESOLVE_LIB_DEFECTS 136 // 2975. Missing case for pair construction in polymorphic allocators 137 template<typename _Up> 138 struct __not_pair { using type = void; }; 139 140 template<typename _Up1, typename _Up2> 141 struct __not_pair<pair<_Up1, _Up2>> { }; 142 143 public: 144 using value_type = _Tp; 145 146 polymorphic_allocator() noexcept 147 : _M_resource(get_default_resource()) 148 { } 149 150 polymorphic_allocator(memory_resource* __r) noexcept 151 __attribute__((__nonnull__)) 152 : _M_resource(__r) 153 { _GLIBCXX_DEBUG_ASSERT(__r); } 154 155 polymorphic_allocator(const polymorphic_allocator& __other) = default; 156 157 template<typename _Up> 158 polymorphic_allocator(const polymorphic_allocator<_Up>& __x) noexcept 159 : _M_resource(__x.resource()) 160 { } 161 162 polymorphic_allocator& 163 operator=(const polymorphic_allocator&) = delete; 164 165 [[nodiscard]] 166 _Tp* 167 allocate(size_t __n) 168 __attribute__((__returns_nonnull__)) 169 { 170 if (__n > (numeric_limits<size_t>::max() / sizeof(_Tp))) 171 std::__throw_bad_alloc(); 172 return static_cast<_Tp*>(_M_resource->allocate(__n * sizeof(_Tp), 173 alignof(_Tp))); 174 } 175 176 void 177 deallocate(_Tp* __p, size_t __n) noexcept 178 __attribute__((__nonnull__)) 179 { _M_resource->deallocate(__p, __n * sizeof(_Tp), alignof(_Tp)); } 180 181#if __cplusplus > 201703L 182 void* 183 allocate_bytes(size_t __nbytes, 184 size_t __alignment = alignof(max_align_t)) 185 { return _M_resource->allocate(__nbytes, __alignment); } 186 187 void 188 deallocate_bytes(void* __p, size_t __nbytes, 189 size_t __alignment = alignof(max_align_t)) 190 { _M_resource->deallocate(__p, __nbytes, __alignment); } 191 192 template<typename _Up> 193 _Up* 194 allocate_object(size_t __n = 1) 195 { 196 if ((std::numeric_limits<size_t>::max() / sizeof(_Up)) < __n) 197 __throw_length_error("polymorphic_allocator::allocate_object"); 198 return static_cast<_Up*>(allocate_bytes(__n * sizeof(_Up), 199 alignof(_Up))); 200 } 201 202 template<typename _Up> 203 void 204 deallocate_object(_Up* __p, size_t __n = 1) 205 { deallocate_bytes(__p, __n * sizeof(_Up), alignof(_Up)); } 206 207 template<typename _Up, typename... _CtorArgs> 208 _Up* 209 new_object(_CtorArgs&&... __ctor_args) 210 { 211 _Up* __p = allocate_object<_Up>(); 212 __try 213 { 214 construct(__p, std::forward<_CtorArgs>(__ctor_args)...); 215 } 216 __catch (...) 217 { 218 deallocate_object(__p); 219 __throw_exception_again; 220 } 221 return __p; 222 } 223 224 template<typename _Up> 225 void 226 delete_object(_Up* __p) 227 { 228 destroy(__p); 229 deallocate_object(__p); 230 } 231#endif // C++2a 232 233#if __cplusplus == 201703L 234 template<typename _Tp1, typename... _Args> 235 __attribute__((__nonnull__)) 236 typename __not_pair<_Tp1>::type 237 construct(_Tp1* __p, _Args&&... __args) 238 { 239 // _GLIBCXX_RESOLVE_LIB_DEFECTS 240 // 2969. polymorphic_allocator::construct() shouldn't pass resource() 241 using __use_tag 242 = std::__uses_alloc_t<_Tp1, polymorphic_allocator, _Args...>; 243 if constexpr (is_base_of_v<__uses_alloc0, __use_tag>) 244 ::new(__p) _Tp1(std::forward<_Args>(__args)...); 245 else if constexpr (is_base_of_v<__uses_alloc1_, __use_tag>) 246 ::new(__p) _Tp1(allocator_arg, *this, 247 std::forward<_Args>(__args)...); 248 else 249 ::new(__p) _Tp1(std::forward<_Args>(__args)..., *this); 250 } 251 252 template<typename _Tp1, typename _Tp2, 253 typename... _Args1, typename... _Args2> 254 __attribute__((__nonnull__)) 255 void 256 construct(pair<_Tp1, _Tp2>* __p, piecewise_construct_t, 257 tuple<_Args1...> __x, tuple<_Args2...> __y) 258 { 259 auto __x_tag = 260 __use_alloc<_Tp1, polymorphic_allocator, _Args1...>(*this); 261 auto __y_tag = 262 __use_alloc<_Tp2, polymorphic_allocator, _Args2...>(*this); 263 index_sequence_for<_Args1...> __x_i; 264 index_sequence_for<_Args2...> __y_i; 265 266 ::new(__p) pair<_Tp1, _Tp2>(piecewise_construct, 267 _S_construct_p(__x_tag, __x_i, __x), 268 _S_construct_p(__y_tag, __y_i, __y)); 269 } 270 271 template<typename _Tp1, typename _Tp2> 272 __attribute__((__nonnull__)) 273 void 274 construct(pair<_Tp1, _Tp2>* __p) 275 { this->construct(__p, piecewise_construct, tuple<>(), tuple<>()); } 276 277 template<typename _Tp1, typename _Tp2, typename _Up, typename _Vp> 278 __attribute__((__nonnull__)) 279 void 280 construct(pair<_Tp1, _Tp2>* __p, _Up&& __x, _Vp&& __y) 281 { 282 this->construct(__p, piecewise_construct, 283 forward_as_tuple(std::forward<_Up>(__x)), 284 forward_as_tuple(std::forward<_Vp>(__y))); 285 } 286 287 template <typename _Tp1, typename _Tp2, typename _Up, typename _Vp> 288 __attribute__((__nonnull__)) 289 void 290 construct(pair<_Tp1, _Tp2>* __p, const std::pair<_Up, _Vp>& __pr) 291 { 292 this->construct(__p, piecewise_construct, 293 forward_as_tuple(__pr.first), 294 forward_as_tuple(__pr.second)); 295 } 296 297 template<typename _Tp1, typename _Tp2, typename _Up, typename _Vp> 298 __attribute__((__nonnull__)) 299 void 300 construct(pair<_Tp1, _Tp2>* __p, pair<_Up, _Vp>&& __pr) 301 { 302 this->construct(__p, piecewise_construct, 303 forward_as_tuple(std::forward<_Up>(__pr.first)), 304 forward_as_tuple(std::forward<_Vp>(__pr.second))); 305 } 306#else 307 template<typename _Tp1, typename... _Args> 308 __attribute__((__nonnull__)) 309 void 310 construct(_Tp1* __p, _Args&&... __args) 311 { 312 std::uninitialized_construct_using_allocator(__p, *this, 313 std::forward<_Args>(__args)...); 314 } 315#endif 316 317 template<typename _Up> 318 __attribute__((__nonnull__)) 319 void 320 destroy(_Up* __p) 321 { __p->~_Up(); } 322 323 polymorphic_allocator 324 select_on_container_copy_construction() const noexcept 325 { return polymorphic_allocator(); } 326 327 memory_resource* 328 resource() const noexcept 329 __attribute__((__returns_nonnull__)) 330 { return _M_resource; } 331 332 private: 333 using __uses_alloc1_ = __uses_alloc1<polymorphic_allocator>; 334 using __uses_alloc2_ = __uses_alloc2<polymorphic_allocator>; 335 336 template<typename _Ind, typename... _Args> 337 static tuple<_Args&&...> 338 _S_construct_p(__uses_alloc0, _Ind, tuple<_Args...>& __t) 339 { return std::move(__t); } 340 341 template<size_t... _Ind, typename... _Args> 342 static tuple<allocator_arg_t, polymorphic_allocator, _Args&&...> 343 _S_construct_p(__uses_alloc1_ __ua, index_sequence<_Ind...>, 344 tuple<_Args...>& __t) 345 { 346 return { 347 allocator_arg, *__ua._M_a, std::get<_Ind>(std::move(__t))... 348 }; 349 } 350 351 template<size_t... _Ind, typename... _Args> 352 static tuple<_Args&&..., polymorphic_allocator> 353 _S_construct_p(__uses_alloc2_ __ua, index_sequence<_Ind...>, 354 tuple<_Args...>& __t) 355 { return { std::get<_Ind>(std::move(__t))..., *__ua._M_a }; } 356 357 memory_resource* _M_resource; 358 }; 359 360 template<typename _Tp1, typename _Tp2> 361 inline bool 362 operator==(const polymorphic_allocator<_Tp1>& __a, 363 const polymorphic_allocator<_Tp2>& __b) noexcept 364 { return *__a.resource() == *__b.resource(); } 365 366 template<typename _Tp1, typename _Tp2> 367 inline bool 368 operator!=(const polymorphic_allocator<_Tp1>& __a, 369 const polymorphic_allocator<_Tp2>& __b) noexcept 370 { return !(__a == __b); } 371 372 373 /// Parameters for tuning a pool resource's behaviour. 374 struct pool_options 375 { 376 /** @brief Upper limit on number of blocks in a chunk. 377 * 378 * A lower value prevents allocating huge chunks that could remain mostly 379 * unused, but means pools will need to replenished more frequently. 380 */ 381 size_t max_blocks_per_chunk = 0; 382 383 /* @brief Largest block size (in bytes) that should be served from pools. 384 * 385 * Larger allocations will be served directly by the upstream resource, 386 * not from one of the pools managed by the pool resource. 387 */ 388 size_t largest_required_pool_block = 0; 389 }; 390 391 // Common implementation details for un-/synchronized pool resources. 392 class __pool_resource 393 { 394 friend class synchronized_pool_resource; 395 friend class unsynchronized_pool_resource; 396 397 __pool_resource(const pool_options& __opts, memory_resource* __upstream); 398 399 ~__pool_resource(); 400 401 __pool_resource(const __pool_resource&) = delete; 402 __pool_resource& operator=(const __pool_resource&) = delete; 403 404 // Allocate a large unpooled block. 405 void* 406 allocate(size_t __bytes, size_t __alignment); 407 408 // Deallocate a large unpooled block. 409 void 410 deallocate(void* __p, size_t __bytes, size_t __alignment); 411 412 413 // Deallocate unpooled memory. 414 void release() noexcept; 415 416 memory_resource* resource() const noexcept 417 { return _M_unpooled.get_allocator().resource(); } 418 419 struct _Pool; 420 421 _Pool* _M_alloc_pools(); 422 423 const pool_options _M_opts; 424 425 struct _BigBlock; 426 // Collection of blocks too big for any pool, sorted by address. 427 // This also stores the only copy of the upstream memory resource pointer. 428 _GLIBCXX_STD_C::pmr::vector<_BigBlock> _M_unpooled; 429 430 const int _M_npools; 431 }; 432 433#ifdef _GLIBCXX_HAS_GTHREADS 434 /// A thread-safe memory resource that manages pools of fixed-size blocks. 435 class synchronized_pool_resource : public memory_resource 436 { 437 public: 438 synchronized_pool_resource(const pool_options& __opts, 439 memory_resource* __upstream) 440 __attribute__((__nonnull__)); 441 442 synchronized_pool_resource() 443 : synchronized_pool_resource(pool_options(), get_default_resource()) 444 { } 445 446 explicit 447 synchronized_pool_resource(memory_resource* __upstream) 448 __attribute__((__nonnull__)) 449 : synchronized_pool_resource(pool_options(), __upstream) 450 { } 451 452 explicit 453 synchronized_pool_resource(const pool_options& __opts) 454 : synchronized_pool_resource(__opts, get_default_resource()) { } 455 456 synchronized_pool_resource(const synchronized_pool_resource&) = delete; 457 458 virtual ~synchronized_pool_resource(); 459 460 synchronized_pool_resource& 461 operator=(const synchronized_pool_resource&) = delete; 462 463 void release(); 464 465 memory_resource* 466 upstream_resource() const noexcept 467 __attribute__((__returns_nonnull__)) 468 { return _M_impl.resource(); } 469 470 pool_options options() const noexcept { return _M_impl._M_opts; } 471 472 protected: 473 void* 474 do_allocate(size_t __bytes, size_t __alignment) override; 475 476 void 477 do_deallocate(void* __p, size_t __bytes, size_t __alignment) override; 478 479 bool 480 do_is_equal(const memory_resource& __other) const noexcept override 481 { return this == &__other; } 482 483 public: 484 // Thread-specific pools (only public for access by implementation details) 485 struct _TPools; 486 487 private: 488 _TPools* _M_alloc_tpools(lock_guard<shared_mutex>&); 489 _TPools* _M_alloc_shared_tpools(lock_guard<shared_mutex>&); 490 auto _M_thread_specific_pools() noexcept; 491 492 __pool_resource _M_impl; 493 __gthread_key_t _M_key; 494 // Linked list of thread-specific pools. All threads share _M_tpools[0]. 495 _TPools* _M_tpools = nullptr; 496 mutable shared_mutex _M_mx; 497 }; 498#endif 499 500 /// A non-thread-safe memory resource that manages pools of fixed-size blocks. 501 class unsynchronized_pool_resource : public memory_resource 502 { 503 public: 504 [[__gnu__::__nonnull__]] 505 unsynchronized_pool_resource(const pool_options& __opts, 506 memory_resource* __upstream); 507 508 unsynchronized_pool_resource() 509 : unsynchronized_pool_resource(pool_options(), get_default_resource()) 510 { } 511 512 [[__gnu__::__nonnull__]] 513 explicit 514 unsynchronized_pool_resource(memory_resource* __upstream) 515 : unsynchronized_pool_resource(pool_options(), __upstream) 516 { } 517 518 explicit 519 unsynchronized_pool_resource(const pool_options& __opts) 520 : unsynchronized_pool_resource(__opts, get_default_resource()) { } 521 522 unsynchronized_pool_resource(const unsynchronized_pool_resource&) = delete; 523 524 virtual ~unsynchronized_pool_resource(); 525 526 unsynchronized_pool_resource& 527 operator=(const unsynchronized_pool_resource&) = delete; 528 529 void release(); 530 531 [[__gnu__::__returns_nonnull__]] 532 memory_resource* 533 upstream_resource() const noexcept 534 { return _M_impl.resource(); } 535 536 pool_options options() const noexcept { return _M_impl._M_opts; } 537 538 protected: 539 void* 540 do_allocate(size_t __bytes, size_t __alignment) override; 541 542 void 543 do_deallocate(void* __p, size_t __bytes, size_t __alignment) override; 544 545 bool 546 do_is_equal(const memory_resource& __other) const noexcept override 547 { return this == &__other; } 548 549 private: 550 using _Pool = __pool_resource::_Pool; 551 552 auto _M_find_pool(size_t) noexcept; 553 554 __pool_resource _M_impl; 555 _Pool* _M_pools = nullptr; 556 }; 557 558 class monotonic_buffer_resource : public memory_resource 559 { 560 public: 561 explicit 562 monotonic_buffer_resource(memory_resource* __upstream) noexcept 563 __attribute__((__nonnull__)) 564 : _M_upstream(__upstream) 565 { _GLIBCXX_DEBUG_ASSERT(__upstream != nullptr); } 566 567 monotonic_buffer_resource(size_t __initial_size, 568 memory_resource* __upstream) noexcept 569 __attribute__((__nonnull__)) 570 : _M_next_bufsiz(__initial_size), 571 _M_upstream(__upstream) 572 { 573 _GLIBCXX_DEBUG_ASSERT(__upstream != nullptr); 574 _GLIBCXX_DEBUG_ASSERT(__initial_size > 0); 575 } 576 577 monotonic_buffer_resource(void* __buffer, size_t __buffer_size, 578 memory_resource* __upstream) noexcept 579 __attribute__((__nonnull__(4))) 580 : _M_current_buf(__buffer), _M_avail(__buffer_size), 581 _M_next_bufsiz(_S_next_bufsize(__buffer_size)), 582 _M_upstream(__upstream), 583 _M_orig_buf(__buffer), _M_orig_size(__buffer_size) 584 { 585 _GLIBCXX_DEBUG_ASSERT(__upstream != nullptr); 586 _GLIBCXX_DEBUG_ASSERT(__buffer != nullptr || __buffer_size == 0); 587 } 588 589 monotonic_buffer_resource() noexcept 590 : monotonic_buffer_resource(get_default_resource()) 591 { } 592 593 explicit 594 monotonic_buffer_resource(size_t __initial_size) noexcept 595 : monotonic_buffer_resource(__initial_size, get_default_resource()) 596 { } 597 598 monotonic_buffer_resource(void* __buffer, size_t __buffer_size) noexcept 599 : monotonic_buffer_resource(__buffer, __buffer_size, get_default_resource()) 600 { } 601 602 monotonic_buffer_resource(const monotonic_buffer_resource&) = delete; 603 604 virtual ~monotonic_buffer_resource(); // key function 605 606 monotonic_buffer_resource& 607 operator=(const monotonic_buffer_resource&) = delete; 608 609 void 610 release() noexcept 611 { 612 if (_M_head) 613 _M_release_buffers(); 614 615 // reset to initial state at contruction: 616 if ((_M_current_buf = _M_orig_buf)) 617 { 618 _M_avail = _M_orig_size; 619 _M_next_bufsiz = _S_next_bufsize(_M_orig_size); 620 } 621 else 622 { 623 _M_avail = 0; 624 _M_next_bufsiz = _M_orig_size; 625 } 626 } 627 628 memory_resource* 629 upstream_resource() const noexcept 630 __attribute__((__returns_nonnull__)) 631 { return _M_upstream; } 632 633 protected: 634 void* 635 do_allocate(size_t __bytes, size_t __alignment) override 636 { 637 if (__bytes == 0) 638 __bytes = 1; // Ensures we don't return the same pointer twice. 639 640 void* __p = std::align(__alignment, __bytes, _M_current_buf, _M_avail); 641 if (!__p) 642 { 643 _M_new_buffer(__bytes, __alignment); 644 __p = _M_current_buf; 645 } 646 _M_current_buf = (char*)_M_current_buf + __bytes; 647 _M_avail -= __bytes; 648 return __p; 649 } 650 651 void 652 do_deallocate(void*, size_t, size_t) override 653 { } 654 655 bool 656 do_is_equal(const memory_resource& __other) const noexcept override 657 { return this == &__other; } 658 659 private: 660 // Update _M_current_buf and _M_avail to refer to a new buffer with 661 // at least the specified size and alignment, allocated from upstream. 662 void 663 _M_new_buffer(size_t __bytes, size_t __alignment); 664 665 // Deallocate all buffers obtained from upstream. 666 void 667 _M_release_buffers() noexcept; 668 669 static size_t 670 _S_next_bufsize(size_t __buffer_size) noexcept 671 { 672 if (__buffer_size == 0) 673 __buffer_size = 1; 674 return __buffer_size * _S_growth_factor; 675 } 676 677 static constexpr size_t _S_init_bufsize = 128 * sizeof(void*); 678 static constexpr float _S_growth_factor = 1.5; 679 680 void* _M_current_buf = nullptr; 681 size_t _M_avail = 0; 682 size_t _M_next_bufsiz = _S_init_bufsize; 683 684 // Initial values set at construction and reused by release(): 685 memory_resource* const _M_upstream; 686 void* const _M_orig_buf = nullptr; 687 size_t const _M_orig_size = _M_next_bufsiz; 688 689 class _Chunk; 690 _Chunk* _M_head = nullptr; 691 }; 692 693} // namespace pmr 694_GLIBCXX_END_NAMESPACE_VERSION 695} // namespace std 696 697#endif // C++17 698#endif // _GLIBCXX_MEMORY_RESOURCE 699