libstdc++
memory_resource
Go to the documentation of this file.
1 // <memory_resource> -*- C++ -*-
2 
3 // Copyright (C) 2018-2025 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  * This header declares the @ref pmr (std::pmr) memory resources.
29  * @ingroup pmr
30  */
31 
32 #ifndef _GLIBCXX_MEMORY_RESOURCE
33 #define _GLIBCXX_MEMORY_RESOURCE 1
34 
35 #ifdef _GLIBCXX_SYSHDR
36 #pragma GCC system_header
37 #endif
38 
39 #include <bits/requires_hosted.h> // polymorphic allocation
40 
41 #define __glibcxx_want_polymorphic_allocator
42 #define __glibcxx_want_memory_resource
43 #include <bits/version.h>
44 
45 #if __cplusplus >= 201703L
46 
47 /**
48  * @defgroup pmr Polymorphic memory resources
49  *
50  * @anchor pmr
51  * @ingroup memory
52  * @since C++17
53  *
54  * Memory resources are classes that implement the `std::pmr::memory_resource`
55  * interface for allocating and deallocating memory. Unlike traditional C++
56  * allocators, memory resources are not value types and are used via pointers
57  * to the abstract base class. They are only responsible for allocating and
58  * deallocating, not for construction and destruction of objects. As a result,
59  * memory resources just allocate raw memory as type `void*` and are not
60  * templates that allocate/deallocate and construct/destroy a specific type.
61  *
62  * The class template `std::pmr::polymorphic_allocator` is an allocator that
63  * uses a memory resource for its allocations.
64  */
65 
66 #include <bits/memory_resource.h>
67 #include <vector> // vector
68 #include <shared_mutex> // shared_mutex
69 #include <bits/align.h> // align
70 #include <debug/assertions.h>
71 
72 namespace std _GLIBCXX_VISIBILITY(default)
73 {
74 _GLIBCXX_BEGIN_NAMESPACE_VERSION
75 namespace pmr
76 {
77 
78 #ifdef __cpp_lib_polymorphic_allocator // C++ >= 20 && HOSTED
79  template<typename _Tp = std::byte>
80  class polymorphic_allocator;
81 #endif
82 
83  // Global memory resources
84 
85  /// A pmr::memory_resource that uses `new` to allocate memory
86  /**
87  * @ingroup pmr
88  * @headerfile memory_resource
89  * @since C++17
90  */
91  [[nodiscard, __gnu__::__returns_nonnull__, __gnu__::__const__]]
92  memory_resource*
93  new_delete_resource() noexcept;
94 
95  /// A pmr::memory_resource that always throws `bad_alloc`
96  [[nodiscard, __gnu__::__returns_nonnull__, __gnu__::__const__]]
97  memory_resource*
98  null_memory_resource() noexcept;
99 
100  /// Replace the default memory resource pointer
101  [[__gnu__::__returns_nonnull__]]
102  memory_resource*
103  set_default_resource(memory_resource* __r) noexcept;
104 
105  /// Get the current default memory resource pointer
106  [[__gnu__::__returns_nonnull__]]
107  memory_resource*
108  get_default_resource() noexcept;
109 
110  // Pool resource classes
111  struct pool_options;
112 #if __cpp_lib_memory_resource >= 201603L // C++ >= 17 && hosted && gthread
113  class synchronized_pool_resource;
114 #endif
115  class unsynchronized_pool_resource;
116  class monotonic_buffer_resource;
117 
118  /// Parameters for tuning a pool resource's behaviour.
119  /**
120  * @ingroup pmr
121  * @headerfile memory_resource
122  * @since C++17
123  */
124  struct pool_options
125  {
126  /** @brief Upper limit on number of blocks in a chunk.
127  *
128  * A lower value prevents allocating huge chunks that could remain mostly
129  * unused, but means pools will need to replenished more frequently.
130  */
131  size_t max_blocks_per_chunk = 0;
132 
133  /* @brief Largest block size (in bytes) that should be served from pools.
134  *
135  * Larger allocations will be served directly by the upstream resource,
136  * not from one of the pools managed by the pool resource.
137  */
138  size_t largest_required_pool_block = 0;
139  };
140 
141  // Common implementation details for un-/synchronized pool resources.
142  class __pool_resource
143  {
144  friend class synchronized_pool_resource;
145  friend class unsynchronized_pool_resource;
146 
147  __pool_resource(const pool_options& __opts, memory_resource* __upstream);
148 
149  ~__pool_resource();
150 
151  __pool_resource(const __pool_resource&) = delete;
152  __pool_resource& operator=(const __pool_resource&) = delete;
153 
154  // Allocate a large unpooled block.
155  void*
156  allocate(size_t __bytes, size_t __alignment);
157 
158  // Deallocate a large unpooled block.
159  void
160  deallocate(void* __p, size_t __bytes, size_t __alignment);
161 
162 
163  // Deallocate unpooled memory.
164  void release() noexcept;
165 
166  memory_resource* resource() const noexcept
167  { return _M_unpooled.get_allocator().resource(); }
168 
169  struct _Pool;
170 
171  _Pool* _M_alloc_pools();
172 
173  const pool_options _M_opts;
174 
175  struct _BigBlock;
176  // Collection of blocks too big for any pool, sorted by address.
177  // This also stores the only copy of the upstream memory resource pointer.
178  _GLIBCXX_STD_C::pmr::vector<_BigBlock> _M_unpooled;
179 
180  const int _M_npools;
181  };
182 
183 #if __cpp_lib_memory_resource >= 201603L // C++ >= 17 && hosted && gthread
184  /// A thread-safe memory resource that manages pools of fixed-size blocks.
185  /**
186  * @ingroup pmr
187  * @headerfile memory_resource
188  * @since C++17
189  */
190  class synchronized_pool_resource : public memory_resource
191  {
192  public:
193  synchronized_pool_resource(const pool_options& __opts,
194  memory_resource* __upstream)
195  __attribute__((__nonnull__));
196 
197  synchronized_pool_resource()
198  : synchronized_pool_resource(pool_options(), get_default_resource())
199  { }
200 
201  explicit
202  synchronized_pool_resource(memory_resource* __upstream)
203  __attribute__((__nonnull__))
204  : synchronized_pool_resource(pool_options(), __upstream)
205  { }
206 
207  explicit
208  synchronized_pool_resource(const pool_options& __opts)
209  : synchronized_pool_resource(__opts, get_default_resource()) { }
210 
211  synchronized_pool_resource(const synchronized_pool_resource&) = delete;
212 
213  virtual ~synchronized_pool_resource();
214 
215  synchronized_pool_resource&
216  operator=(const synchronized_pool_resource&) = delete;
217 
218  void release();
219 
220  memory_resource*
221  upstream_resource() const noexcept
222  __attribute__((__returns_nonnull__))
223  { return _M_impl.resource(); }
224 
225  pool_options options() const noexcept { return _M_impl._M_opts; }
226 
227  protected:
228  void*
229  do_allocate(size_t __bytes, size_t __alignment) override;
230 
231  void
232  do_deallocate(void* __p, size_t __bytes, size_t __alignment) override;
233 
234  bool
235  do_is_equal(const memory_resource& __other) const noexcept override
236  { return this == &__other; }
237 
238  public:
239  // Thread-specific pools (only public for access by implementation details)
240  struct _TPools;
241 
242  private:
243  _TPools* _M_alloc_tpools(lock_guard<shared_mutex>&);
244  _TPools* _M_alloc_shared_tpools(lock_guard<shared_mutex>&);
245  auto _M_thread_specific_pools() noexcept;
246 
247  __pool_resource _M_impl;
248  __gthread_key_t _M_key;
249  // Linked list of thread-specific pools. All threads share _M_tpools[0].
250  _TPools* _M_tpools = nullptr;
251  mutable shared_mutex _M_mx;
252  };
253 #endif // __cpp_lib_memory_resource >= 201603L
254 
255  /// A non-thread-safe memory resource that manages pools of fixed-size blocks.
256  /**
257  * @ingroup pmr
258  * @headerfile memory_resource
259  * @since C++17
260  */
261  class unsynchronized_pool_resource : public memory_resource
262  {
263  public:
264  [[__gnu__::__nonnull__]]
265  unsynchronized_pool_resource(const pool_options& __opts,
266  memory_resource* __upstream);
267 
268  unsynchronized_pool_resource()
269  : unsynchronized_pool_resource(pool_options(), get_default_resource())
270  { }
271 
272  [[__gnu__::__nonnull__]]
273  explicit
274  unsynchronized_pool_resource(memory_resource* __upstream)
275  : unsynchronized_pool_resource(pool_options(), __upstream)
276  { }
277 
278  explicit
279  unsynchronized_pool_resource(const pool_options& __opts)
280  : unsynchronized_pool_resource(__opts, get_default_resource()) { }
281 
282  unsynchronized_pool_resource(const unsynchronized_pool_resource&) = delete;
283 
284  virtual ~unsynchronized_pool_resource();
285 
286  unsynchronized_pool_resource&
287  operator=(const unsynchronized_pool_resource&) = delete;
288 
289  void release();
290 
291  [[__gnu__::__returns_nonnull__]]
292  memory_resource*
293  upstream_resource() const noexcept
294  { return _M_impl.resource(); }
295 
296  pool_options options() const noexcept { return _M_impl._M_opts; }
297 
298  protected:
299  void*
300  do_allocate(size_t __bytes, size_t __alignment) override;
301 
302  void
303  do_deallocate(void* __p, size_t __bytes, size_t __alignment) override;
304 
305  bool
306  do_is_equal(const memory_resource& __other) const noexcept override
307  { return this == &__other; }
308 
309  private:
310  using _Pool = __pool_resource::_Pool;
311 
312  auto _M_find_pool(size_t) noexcept;
313 
314  __pool_resource _M_impl;
315  _Pool* _M_pools = nullptr;
316  };
317 
318  /// A memory resource that allocates from a fixed-size buffer.
319  /**
320  * The main feature of a `pmr::monotonic_buffer_resource` is that its
321  * `do_deallocate` does nothing. This makes it very fast because there is no
322  * need to manage a free list, and every allocation simply returns a new
323  * block of memory, rather than searching for a suitably-sized free block.
324  * Because deallocating is a no-op, the amount of memory used by the resource
325  * only grows until `release()` (or the destructor) is called to return all
326  * memory to upstream.
327  *
328  * A `monotonic_buffer_resource` can be initialized with a buffer that
329  * will be used to satisfy all allocation requests, until the buffer is full.
330  * After that a new buffer will be allocated from the upstream resource.
331  * By using a stack buffer and `pmr::null_memory_resource()` as the upstream
332  * you can get a memory resource that only uses the stack and never
333  * dynamically allocates.
334  *
335  * @ingroup pmr
336  * @headerfile memory_resource
337  * @since C++17
338  */
339  class monotonic_buffer_resource : public memory_resource
340  {
341  public:
342  explicit
343  monotonic_buffer_resource(memory_resource* __upstream) noexcept
344  __attribute__((__nonnull__))
345  : _M_upstream(__upstream)
346  { _GLIBCXX_DEBUG_ASSERT(__upstream != nullptr); }
347 
348  monotonic_buffer_resource(size_t __initial_size,
349  memory_resource* __upstream) noexcept
350  __attribute__((__nonnull__))
351  : _M_next_bufsiz(__initial_size),
352  _M_upstream(__upstream)
353  {
354  _GLIBCXX_DEBUG_ASSERT(__upstream != nullptr);
355  _GLIBCXX_DEBUG_ASSERT(__initial_size > 0);
356  }
357 
358  monotonic_buffer_resource(void* __buffer, size_t __buffer_size,
359  memory_resource* __upstream) noexcept
360  __attribute__((__nonnull__(4)))
361  : _M_current_buf(__buffer), _M_avail(__buffer_size),
362  _M_next_bufsiz(_S_next_bufsize(__buffer_size)),
363  _M_upstream(__upstream),
364  _M_orig_buf(__buffer), _M_orig_size(__buffer_size)
365  {
366  _GLIBCXX_DEBUG_ASSERT(__upstream != nullptr);
367  _GLIBCXX_DEBUG_ASSERT(__buffer != nullptr || __buffer_size == 0);
368  }
369 
370  monotonic_buffer_resource() noexcept
371  : monotonic_buffer_resource(get_default_resource())
372  { }
373 
374  explicit
375  monotonic_buffer_resource(size_t __initial_size) noexcept
376  : monotonic_buffer_resource(__initial_size, get_default_resource())
377  { }
378 
379  monotonic_buffer_resource(void* __buffer, size_t __buffer_size) noexcept
380  : monotonic_buffer_resource(__buffer, __buffer_size, get_default_resource())
381  { }
382 
383  monotonic_buffer_resource(const monotonic_buffer_resource&) = delete;
384 
385  virtual ~monotonic_buffer_resource(); // key function
386 
387  monotonic_buffer_resource&
388  operator=(const monotonic_buffer_resource&) = delete;
389 
390  void
391  release() noexcept
392  {
393  if (_M_head)
394  _M_release_buffers();
395 
396  // reset to initial state at contruction:
397  if ((_M_current_buf = _M_orig_buf))
398  {
399  _M_avail = _M_orig_size;
400  _M_next_bufsiz = _S_next_bufsize(_M_orig_size);
401  }
402  else
403  {
404  _M_avail = 0;
405  _M_next_bufsiz = _M_orig_size;
406  }
407  }
408 
409  memory_resource*
410  upstream_resource() const noexcept
411  __attribute__((__returns_nonnull__))
412  { return _M_upstream; }
413 
414  protected:
415  void*
416  do_allocate(size_t __bytes, size_t __alignment) override
417  {
418  if (__builtin_expect(__bytes == 0, false))
419  __bytes = 1; // Ensures we don't return the same pointer twice.
420 
421  void* __p = std::align(__alignment, __bytes, _M_current_buf, _M_avail);
422  if (__builtin_expect(__p == nullptr, false))
423  {
424  _M_new_buffer(__bytes, __alignment);
425  __p = _M_current_buf;
426  }
427  _M_current_buf = (char*)_M_current_buf + __bytes;
428  _M_avail -= __bytes;
429  return __p;
430  }
431 
432  void
433  do_deallocate(void*, size_t, size_t) override
434  { }
435 
436  bool
437  do_is_equal(const memory_resource& __other) const noexcept override
438  { return this == &__other; }
439 
440  private:
441  // Update _M_current_buf and _M_avail to refer to a new buffer with
442  // at least the specified size and alignment, allocated from upstream.
443  void
444  _M_new_buffer(size_t __bytes, size_t __alignment);
445 
446  // Deallocate all buffers obtained from upstream.
447  void
448  _M_release_buffers() noexcept;
449 
450  static size_t
451  _S_next_bufsize(size_t __buffer_size) noexcept
452  {
453  if (__builtin_expect(__buffer_size == 0, false))
454  __buffer_size = 1;
455  return __buffer_size * _S_growth_factor;
456  }
457 
458  static constexpr size_t _S_init_bufsize = 128 * sizeof(void*);
459  static constexpr float _S_growth_factor = 1.5;
460 
461  void* _M_current_buf = nullptr;
462  size_t _M_avail = 0;
463  size_t _M_next_bufsiz = _S_init_bufsize;
464 
465  // Initial values set at construction and reused by release():
466  memory_resource* const _M_upstream;
467  void* const _M_orig_buf = nullptr;
468  size_t const _M_orig_size = _M_next_bufsiz;
469 
470  class _Chunk;
471  _Chunk* _M_head = nullptr;
472  };
473 
474 } // namespace pmr
475 _GLIBCXX_END_NAMESPACE_VERSION
476 } // namespace std
477 
478 #endif // C++17
479 #endif // _GLIBCXX_MEMORY_RESOURCE