memory_resource 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481
  1. // <memory_resource> -*- C++ -*-
  2. // Copyright (C) 2018-2023 Free Software Foundation, Inc.
  3. //
  4. // This file is part of the GNU ISO C++ Library. This library is free
  5. // software; you can redistribute it and/or modify it under the
  6. // terms of the GNU General Public License as published by the
  7. // Free Software Foundation; either version 3, or (at your option)
  8. // any later version.
  9. // This library is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU General Public License for more details.
  13. // Under Section 7 of GPL version 3, you are granted additional
  14. // permissions described in the GCC Runtime Library Exception, version
  15. // 3.1, as published by the Free Software Foundation.
  16. // You should have received a copy of the GNU General Public License and
  17. // a copy of the GCC Runtime Library Exception along with this program;
  18. // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
  19. // <http://www.gnu.org/licenses/>.
  20. /** @file include/memory_resource
  21. * This is a Standard C++ Library header.
  22. *
  23. * This header declares the @ref pmr (std::pmr) memory resources.
  24. * @ingroup pmr
  25. */
  26. #ifndef _GLIBCXX_MEMORY_RESOURCE
  27. #define _GLIBCXX_MEMORY_RESOURCE 1
  28. #pragma GCC system_header
  29. #include <bits/requires_hosted.h> // polymorphic allocation
  30. #if __cplusplus >= 201703L
  31. /**
  32. * @defgroup pmr Polymorphic memory resources
  33. *
  34. * @anchor pmr
  35. * @ingroup memory
  36. * @since C++17
  37. *
  38. * Memory resources are classes that implement the `std::pmr::memory_resource`
  39. * interface for allocating and deallocating memory. Unlike traditional C++
  40. * allocators, memory resources are not value types and are used via pointers
  41. * to the abstract base class. They are only responsible for allocating and
  42. * deallocating, not for construction and destruction of objects. As a result,
  43. * memory resources just allocate raw memory as type `void*` and are not
  44. * templates that allocate/deallocate and construct/destroy a specific type.
  45. *
  46. * The class template `std::pmr::polymorphic_allocator` is an allocator that
  47. * uses a memory resource for its allocations.
  48. */
  49. #include <bits/memory_resource.h>
  50. #include <vector> // vector
  51. #include <shared_mutex> // shared_mutex
  52. #include <bits/align.h> // align
  53. #include <debug/assertions.h>
  54. namespace std _GLIBCXX_VISIBILITY(default)
  55. {
  56. _GLIBCXX_BEGIN_NAMESPACE_VERSION
  57. namespace pmr
  58. {
  59. #ifdef _GLIBCXX_HAS_GTHREADS
  60. // Header and all contents are present.
  61. # define __cpp_lib_memory_resource 201603L
  62. #else
  63. // The pmr::synchronized_pool_resource type is missing.
  64. # define __cpp_lib_memory_resource 1
  65. #endif
  66. #if __cplusplus >= 202002L
  67. # define __cpp_lib_polymorphic_allocator 201902L
  68. template<typename _Tp = std::byte>
  69. class polymorphic_allocator;
  70. #endif
  71. // Global memory resources
  72. /// A pmr::memory_resource that uses `new` to allocate memory
  73. /**
  74. * @ingroup pmr
  75. * @headerfile memory_resource
  76. * @since C++17
  77. */
  78. [[nodiscard, __gnu__::__returns_nonnull__, __gnu__::__const__]]
  79. memory_resource*
  80. new_delete_resource() noexcept;
  81. /// A pmr::memory_resource that always throws `bad_alloc`
  82. [[nodiscard, __gnu__::__returns_nonnull__, __gnu__::__const__]]
  83. memory_resource*
  84. null_memory_resource() noexcept;
  85. /// Replace the default memory resource pointer
  86. [[__gnu__::__returns_nonnull__]]
  87. memory_resource*
  88. set_default_resource(memory_resource* __r) noexcept;
  89. /// Get the current default memory resource pointer
  90. [[__gnu__::__returns_nonnull__]]
  91. memory_resource*
  92. get_default_resource() noexcept;
  93. // Pool resource classes
  94. struct pool_options;
  95. #ifdef _GLIBCXX_HAS_GTHREADS
  96. class synchronized_pool_resource;
  97. #endif
  98. class unsynchronized_pool_resource;
  99. class monotonic_buffer_resource;
  100. /// Parameters for tuning a pool resource's behaviour.
  101. /**
  102. * @ingroup pmr
  103. * @headerfile memory_resource
  104. * @since C++17
  105. */
  106. struct pool_options
  107. {
  108. /** @brief Upper limit on number of blocks in a chunk.
  109. *
  110. * A lower value prevents allocating huge chunks that could remain mostly
  111. * unused, but means pools will need to replenished more frequently.
  112. */
  113. size_t max_blocks_per_chunk = 0;
  114. /* @brief Largest block size (in bytes) that should be served from pools.
  115. *
  116. * Larger allocations will be served directly by the upstream resource,
  117. * not from one of the pools managed by the pool resource.
  118. */
  119. size_t largest_required_pool_block = 0;
  120. };
  121. // Common implementation details for un-/synchronized pool resources.
  122. class __pool_resource
  123. {
  124. friend class synchronized_pool_resource;
  125. friend class unsynchronized_pool_resource;
  126. __pool_resource(const pool_options& __opts, memory_resource* __upstream);
  127. ~__pool_resource();
  128. __pool_resource(const __pool_resource&) = delete;
  129. __pool_resource& operator=(const __pool_resource&) = delete;
  130. // Allocate a large unpooled block.
  131. void*
  132. allocate(size_t __bytes, size_t __alignment);
  133. // Deallocate a large unpooled block.
  134. void
  135. deallocate(void* __p, size_t __bytes, size_t __alignment);
  136. // Deallocate unpooled memory.
  137. void release() noexcept;
  138. memory_resource* resource() const noexcept
  139. { return _M_unpooled.get_allocator().resource(); }
  140. struct _Pool;
  141. _Pool* _M_alloc_pools();
  142. const pool_options _M_opts;
  143. struct _BigBlock;
  144. // Collection of blocks too big for any pool, sorted by address.
  145. // This also stores the only copy of the upstream memory resource pointer.
  146. _GLIBCXX_STD_C::pmr::vector<_BigBlock> _M_unpooled;
  147. const int _M_npools;
  148. };
  149. #ifdef _GLIBCXX_HAS_GTHREADS
  150. /// A thread-safe memory resource that manages pools of fixed-size blocks.
  151. /**
  152. * @ingroup pmr
  153. * @headerfile memory_resource
  154. * @since C++17
  155. */
  156. class synchronized_pool_resource : public memory_resource
  157. {
  158. public:
  159. synchronized_pool_resource(const pool_options& __opts,
  160. memory_resource* __upstream)
  161. __attribute__((__nonnull__));
  162. synchronized_pool_resource()
  163. : synchronized_pool_resource(pool_options(), get_default_resource())
  164. { }
  165. explicit
  166. synchronized_pool_resource(memory_resource* __upstream)
  167. __attribute__((__nonnull__))
  168. : synchronized_pool_resource(pool_options(), __upstream)
  169. { }
  170. explicit
  171. synchronized_pool_resource(const pool_options& __opts)
  172. : synchronized_pool_resource(__opts, get_default_resource()) { }
  173. synchronized_pool_resource(const synchronized_pool_resource&) = delete;
  174. virtual ~synchronized_pool_resource();
  175. synchronized_pool_resource&
  176. operator=(const synchronized_pool_resource&) = delete;
  177. void release();
  178. memory_resource*
  179. upstream_resource() const noexcept
  180. __attribute__((__returns_nonnull__))
  181. { return _M_impl.resource(); }
  182. pool_options options() const noexcept { return _M_impl._M_opts; }
  183. protected:
  184. void*
  185. do_allocate(size_t __bytes, size_t __alignment) override;
  186. void
  187. do_deallocate(void* __p, size_t __bytes, size_t __alignment) override;
  188. bool
  189. do_is_equal(const memory_resource& __other) const noexcept override
  190. { return this == &__other; }
  191. public:
  192. // Thread-specific pools (only public for access by implementation details)
  193. struct _TPools;
  194. private:
  195. _TPools* _M_alloc_tpools(lock_guard<shared_mutex>&);
  196. _TPools* _M_alloc_shared_tpools(lock_guard<shared_mutex>&);
  197. auto _M_thread_specific_pools() noexcept;
  198. __pool_resource _M_impl;
  199. __gthread_key_t _M_key;
  200. // Linked list of thread-specific pools. All threads share _M_tpools[0].
  201. _TPools* _M_tpools = nullptr;
  202. mutable shared_mutex _M_mx;
  203. };
  204. #endif
  205. /// A non-thread-safe memory resource that manages pools of fixed-size blocks.
  206. /**
  207. * @ingroup pmr
  208. * @headerfile memory_resource
  209. * @since C++17
  210. */
  211. class unsynchronized_pool_resource : public memory_resource
  212. {
  213. public:
  214. [[__gnu__::__nonnull__]]
  215. unsynchronized_pool_resource(const pool_options& __opts,
  216. memory_resource* __upstream);
  217. unsynchronized_pool_resource()
  218. : unsynchronized_pool_resource(pool_options(), get_default_resource())
  219. { }
  220. [[__gnu__::__nonnull__]]
  221. explicit
  222. unsynchronized_pool_resource(memory_resource* __upstream)
  223. : unsynchronized_pool_resource(pool_options(), __upstream)
  224. { }
  225. explicit
  226. unsynchronized_pool_resource(const pool_options& __opts)
  227. : unsynchronized_pool_resource(__opts, get_default_resource()) { }
  228. unsynchronized_pool_resource(const unsynchronized_pool_resource&) = delete;
  229. virtual ~unsynchronized_pool_resource();
  230. unsynchronized_pool_resource&
  231. operator=(const unsynchronized_pool_resource&) = delete;
  232. void release();
  233. [[__gnu__::__returns_nonnull__]]
  234. memory_resource*
  235. upstream_resource() const noexcept
  236. { return _M_impl.resource(); }
  237. pool_options options() const noexcept { return _M_impl._M_opts; }
  238. protected:
  239. void*
  240. do_allocate(size_t __bytes, size_t __alignment) override;
  241. void
  242. do_deallocate(void* __p, size_t __bytes, size_t __alignment) override;
  243. bool
  244. do_is_equal(const memory_resource& __other) const noexcept override
  245. { return this == &__other; }
  246. private:
  247. using _Pool = __pool_resource::_Pool;
  248. auto _M_find_pool(size_t) noexcept;
  249. __pool_resource _M_impl;
  250. _Pool* _M_pools = nullptr;
  251. };
  252. /// A memory resource that allocates from a fixed-size buffer.
  253. /**
  254. * The main feature of a `pmr::monotonic_buffer_resource` is that its
  255. * `do_deallocate` does nothing. This makes it very fast because there is no
  256. * need to manage a free list, and every allocation simply returns a new
  257. * block of memory, rather than searching for a suitably-sized free block.
  258. * Because deallocating is a no-op, the amount of memory used by the resource
  259. * only grows until `release()` (or the destructor) is called to return all
  260. * memory to upstream.
  261. *
  262. * A `monotonic_buffer_resource` can be initialized with a buffer that
  263. * will be used to satisfy all allocation requests, until the buffer is full.
  264. * After that a new buffer will be allocated from the upstream resource.
  265. * By using a stack buffer and `pmr::null_memory_resource()` as the upstream
  266. * you can get a memory resource that only uses the stack and never
  267. * dynamically allocates.
  268. *
  269. * @ingroup pmr
  270. * @headerfile memory_resource
  271. * @since C++17
  272. */
  273. class monotonic_buffer_resource : public memory_resource
  274. {
  275. public:
  276. explicit
  277. monotonic_buffer_resource(memory_resource* __upstream) noexcept
  278. __attribute__((__nonnull__))
  279. : _M_upstream(__upstream)
  280. { _GLIBCXX_DEBUG_ASSERT(__upstream != nullptr); }
  281. monotonic_buffer_resource(size_t __initial_size,
  282. memory_resource* __upstream) noexcept
  283. __attribute__((__nonnull__))
  284. : _M_next_bufsiz(__initial_size),
  285. _M_upstream(__upstream)
  286. {
  287. _GLIBCXX_DEBUG_ASSERT(__upstream != nullptr);
  288. _GLIBCXX_DEBUG_ASSERT(__initial_size > 0);
  289. }
  290. monotonic_buffer_resource(void* __buffer, size_t __buffer_size,
  291. memory_resource* __upstream) noexcept
  292. __attribute__((__nonnull__(4)))
  293. : _M_current_buf(__buffer), _M_avail(__buffer_size),
  294. _M_next_bufsiz(_S_next_bufsize(__buffer_size)),
  295. _M_upstream(__upstream),
  296. _M_orig_buf(__buffer), _M_orig_size(__buffer_size)
  297. {
  298. _GLIBCXX_DEBUG_ASSERT(__upstream != nullptr);
  299. _GLIBCXX_DEBUG_ASSERT(__buffer != nullptr || __buffer_size == 0);
  300. }
  301. monotonic_buffer_resource() noexcept
  302. : monotonic_buffer_resource(get_default_resource())
  303. { }
  304. explicit
  305. monotonic_buffer_resource(size_t __initial_size) noexcept
  306. : monotonic_buffer_resource(__initial_size, get_default_resource())
  307. { }
  308. monotonic_buffer_resource(void* __buffer, size_t __buffer_size) noexcept
  309. : monotonic_buffer_resource(__buffer, __buffer_size, get_default_resource())
  310. { }
  311. monotonic_buffer_resource(const monotonic_buffer_resource&) = delete;
  312. virtual ~monotonic_buffer_resource(); // key function
  313. monotonic_buffer_resource&
  314. operator=(const monotonic_buffer_resource&) = delete;
  315. void
  316. release() noexcept
  317. {
  318. if (_M_head)
  319. _M_release_buffers();
  320. // reset to initial state at contruction:
  321. if ((_M_current_buf = _M_orig_buf))
  322. {
  323. _M_avail = _M_orig_size;
  324. _M_next_bufsiz = _S_next_bufsize(_M_orig_size);
  325. }
  326. else
  327. {
  328. _M_avail = 0;
  329. _M_next_bufsiz = _M_orig_size;
  330. }
  331. }
  332. memory_resource*
  333. upstream_resource() const noexcept
  334. __attribute__((__returns_nonnull__))
  335. { return _M_upstream; }
  336. protected:
  337. void*
  338. do_allocate(size_t __bytes, size_t __alignment) override
  339. {
  340. if (__builtin_expect(__bytes == 0, false))
  341. __bytes = 1; // Ensures we don't return the same pointer twice.
  342. void* __p = std::align(__alignment, __bytes, _M_current_buf, _M_avail);
  343. if (__builtin_expect(__p == nullptr, false))
  344. {
  345. _M_new_buffer(__bytes, __alignment);
  346. __p = _M_current_buf;
  347. }
  348. _M_current_buf = (char*)_M_current_buf + __bytes;
  349. _M_avail -= __bytes;
  350. return __p;
  351. }
  352. void
  353. do_deallocate(void*, size_t, size_t) override
  354. { }
  355. bool
  356. do_is_equal(const memory_resource& __other) const noexcept override
  357. { return this == &__other; }
  358. private:
  359. // Update _M_current_buf and _M_avail to refer to a new buffer with
  360. // at least the specified size and alignment, allocated from upstream.
  361. void
  362. _M_new_buffer(size_t __bytes, size_t __alignment);
  363. // Deallocate all buffers obtained from upstream.
  364. void
  365. _M_release_buffers() noexcept;
  366. static size_t
  367. _S_next_bufsize(size_t __buffer_size) noexcept
  368. {
  369. if (__builtin_expect(__buffer_size == 0, false))
  370. __buffer_size = 1;
  371. return __buffer_size * _S_growth_factor;
  372. }
  373. static constexpr size_t _S_init_bufsize = 128 * sizeof(void*);
  374. static constexpr float _S_growth_factor = 1.5;
  375. void* _M_current_buf = nullptr;
  376. size_t _M_avail = 0;
  377. size_t _M_next_bufsiz = _S_init_bufsize;
  378. // Initial values set at construction and reused by release():
  379. memory_resource* const _M_upstream;
  380. void* const _M_orig_buf = nullptr;
  381. size_t const _M_orig_size = _M_next_bufsiz;
  382. class _Chunk;
  383. _Chunk* _M_head = nullptr;
  384. };
  385. } // namespace pmr
  386. _GLIBCXX_END_NAMESPACE_VERSION
  387. } // namespace std
  388. #endif // C++17
  389. #endif // _GLIBCXX_MEMORY_RESOURCE