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