libstdc++: memory_resource Source File

libstdc++

memory_resource
Go to the documentation of this file.
1// <memory_resource> -*- C++ -*-
2
3// Copyright (C) 2018-2026 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
72namespace std _GLIBCXX_VISIBILITY(default)
73{
74_GLIBCXX_BEGIN_NAMESPACE_VERSION
75namespace 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
std::pmr::set_default_resource
memory_resource * set_default_resource(memory_resource *__r) noexcept
Replace the default memory resource pointer.
std::pmr::null_memory_resource
memory_resource * null_memory_resource() noexcept
A pmr::memory_resource that always throws bad_alloc.
std::pmr::get_default_resource
memory_resource * get_default_resource() noexcept
Get the current default memory resource pointer.
shared_mutex
vector
align.h
version.h
memory_resource.h
requires_hosted.h
assertions.h
std::align
void * align(size_t __align, size_t __size, void *&__ptr, size_t &__space) noexcept
Fit aligned storage in buffer.
Definition align.h:60
std::pmr::new_delete_resource
memory_resource * new_delete_resource() noexcept
A pmr::memory_resource that uses new to allocate memory.
std
ISO C++ entities toplevel namespace is std.
std::pmr::pool_options
Parameters for tuning a pool resource's behaviour.
Definition memory_resource:125
std::pmr::pool_options::max_blocks_per_chunk
size_t max_blocks_per_chunk
Upper limit on number of blocks in a chunk.
Definition memory_resource:131
std::pmr::unsynchronized_pool_resource
A non-thread-safe memory resource that manages pools of fixed-size blocks.
Definition memory_resource:262
std::pmr::monotonic_buffer_resource
A memory resource that allocates from a fixed-size buffer.
Definition memory_resource:340
std::pmr::memory_resource
Class memory_resource.
Definition memory_resource.h:65
std::pmr::polymorphic_allocator
Class template polymorphic_allocator.
Definition memory_resource.h:124