WPILibC++ 2025.1.1
Loading...
Searching...
No Matches
temporary_allocator.hpp
Go to the documentation of this file.
1// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
2// SPDX-License-Identifier: Zlib
3
4#ifndef WPI_MEMORY_TEMPORARY_ALLOCATOR_HPP_INCLUDED
5#define WPI_MEMORY_TEMPORARY_ALLOCATOR_HPP_INCLUDED
6
7/// \file
8/// Class \ref wpi::memory::temporary_allocator and related functions.
9
10#include "config.hpp"
11#include "memory_stack.hpp"
12
13#if WPI_MEMORY_TEMPORARY_STACK_MODE >= 2
14#include <atomic>
15#endif
16
17namespace wpi
18{
19 namespace memory
20 {
21 class temporary_allocator;
22 class temporary_stack;
23
24 namespace detail
25 {
27 {
28 public:
29 explicit temporary_block_allocator(std::size_t block_size) noexcept;
30
32
34
35 std::size_t next_block_size() const noexcept
36 {
37 return block_size_;
38 }
39
40 using growth_tracker = void (*)(std::size_t size);
41
43
45
46 private:
47 growth_tracker tracker_;
48 std::size_t block_size_;
49 };
50
52
53 class temporary_stack_list;
54
55#if WPI_MEMORY_TEMPORARY_STACK_MODE >= 2
57 {
58 public:
59 // doesn't add into list
60 temporary_stack_list_node() noexcept : in_use_(true) {}
61
62 temporary_stack_list_node(int) noexcept;
63
64 ~temporary_stack_list_node() noexcept {}
65
66 private:
67 temporary_stack_list_node* next_ = nullptr;
68 std::atomic<bool> in_use_;
69
70 friend temporary_stack_list;
71 };
72
73 static class temporary_allocator_dtor_t
74 {
75 public:
76 temporary_allocator_dtor_t() noexcept;
77 ~temporary_allocator_dtor_t() noexcept;
78 } temporary_allocator_dtor;
79#else
81 {
82 protected:
84
86
88 };
89#endif
90 } // namespace detail
91
92 /// A wrapper around the \ref memory_stack that is used by the \ref temporary_allocator.
93 /// There should be at least one per-thread.
94 /// \ingroup memory_allocator
95 class temporary_stack : WPI_EBO(detail::temporary_stack_list_node)
96 {
97 public:
98 /// The type of the handler called when the internal \ref memory_stack grows.
99 /// It gets the size of the new block that will be allocated.
100 /// \requiredbe The handler shall log the growth, throw an exception or aborts the program.
101 /// If this function does not return, the growth is prevented but the allocator unusable until memory is freed.
102 /// \defaultbe The default handler does nothing.
104
105 /// \effects Sets \c h as the new \ref growth_tracker.
106 /// A \c nullptr sets the default \ref growth_tracker.
107 /// Each thread has its own, separate tracker.
108 /// \returns The previous \ref growth_tracker. This is never \c nullptr.
110 {
111 return stack_.get_allocator().set_growth_tracker(t);
112 }
113
114 /// \returns The current \ref growth_tracker. This is never \c nullptr.
116 {
117 return stack_.get_allocator().get_growth_tracker();
118 }
119
120 /// \effects Creates it with a given initial size of the stack.
121 /// It can grow if needed, although that is expensive.
122 /// \requires `initial_size` must be greater than `0`.
123 explicit temporary_stack(std::size_t initial_size) : stack_(initial_size), top_(nullptr)
124 {
125 }
126
127 /// \returns `next_capacity()` of the internal `memory_stack`.
128 std::size_t next_capacity() const noexcept
129 {
130 return stack_.next_capacity();
131 }
132
133 private:
134 temporary_stack(int i, std::size_t initial_size)
135 : detail::temporary_stack_list_node(i), stack_(initial_size), top_(nullptr)
136 {
137 }
138
139 using marker = detail::temporary_stack_impl::marker;
140
141 marker top() const noexcept
142 {
143 return stack_.top();
144 }
145
146 void unwind(marker m) noexcept
147 {
148 stack_.unwind(m);
149 }
150
151 detail::temporary_stack_impl stack_;
152 temporary_allocator* top_;
153
154#if !defined(DOXYGEN)
155 friend temporary_allocator;
156 friend memory_stack_raii_unwind<temporary_stack>;
157 friend detail::temporary_stack_list;
158#endif
159 };
160
161 /// Manually takes care of the lifetime of the per-thread \ref temporary_stack.
162 /// The constructor will create it, if not already done, and the destructor will destroy it, if not already done.
163 /// \note If there are multiple objects in a thread,
164 /// this will lead to unnecessary construction and destruction of the stack.
165 /// It is thus adviced to create one object on the top-level function of the thread, e.g. in `main()`.
166 /// \note If `WPI_MEMORY_TEMPORARY_STACK_MODE == 2`, it is not necessary to use this class,
167 /// the nifty counter will clean everything upon program termination.
168 /// But it can still be used as an optimization if you have a thread that is terminated long before program exit.
169 /// The automatic clean up will only occur much later.
170 /// \note If `WPI_MEMORY_TEMPORARY_STACK_MODE == 0`, the use of this class has no effect,
171 /// because the per-thread stack is disabled.
172 /// \relatesalso temporary_stack
174 {
175 public:
176 static constexpr std::size_t default_stack_size = 4096u;
177
178 static const struct defer_create_t
179 {
180 defer_create_t() noexcept {}
181 } defer_create;
182
183 /// \effects Does not create the per-thread stack.
184 /// It will be created by the first call to \ref get_temporary_stack() in the current thread.
185 /// \note If `WPI_MEMORY_TEMPORARY_STACK_MODE == 0`, this function has no effect.
187
188 /// \effects Creates the per-thread stack with the given default size if it wasn't already created.
189 /// \requires `initial_size` must not be `0` if `WPI_MEMORY_TEMPORARY_STACK_MODE != 0`.
190 /// \note If `WPI_MEMORY_TEMPORARY_STACK_MODE == 0`, this function will issue a warning in debug mode.
191 /// This can be disabled by passing `0` as the initial size.
192 temporary_stack_initializer(std::size_t initial_size = default_stack_size);
193
194 /// \effects Destroys the per-thread stack if it isn't already destroyed.
196
199 };
200
201 /// \effects Creates the per-thread \ref temporary_stack with the given initial size,
202 /// if it wasn't already created.
203 /// \returns The per-thread \ref temporary_stack.
204 /// \requires There must be a per-thread temporary stack (\ref WPI_MEMORY_TEMPORARY_STACK_MODE must not be equal to `0`).
205 /// \note If \ref WPI_MEMORY_TEMPORARY_STACK_MODE is equal to `1`,
206 /// this function can create the temporary stack.
207 /// But if there is no \ref temporary_stack_initializer, it won't be destroyed.
208 /// \relatesalso temporary_stack
209 temporary_stack& get_temporary_stack(
210 std::size_t initial_size = temporary_stack_initializer::default_stack_size);
211
212 /// A stateful RawAllocator that handles temporary allocations.
213 /// It works similar to \c alloca() but uses a seperate \ref memory_stack for the allocations,
214 /// instead of the actual program stack.
215 /// This avoids the stack overflow error and is portable,
216 /// with a similar speed.
217 /// All allocations done in the scope of the allocator object are automatically freed when the object is destroyed.
218 /// \ingroup memory_allocator
220 {
221 public:
222 /// \effects Creates it by using the \ref get_temporary_stack() to get the temporary stack.
223 /// \requires There must be a per-thread temporary stack (\ref WPI_MEMORY_TEMPORARY_STACK_MODE must not be equal to `0`).
225
226 /// \effects Creates it by giving it the \ref temporary_stack it uses for allocation.
228
230
233
234 /// \effects Allocates memory from the internal \ref memory_stack by forwarding to it.
235 /// \returns The result of \ref memory_stack::allocate().
236 /// \requires `is_active()` must return `true`.
237 void* allocate(std::size_t size, std::size_t alignment);
238
239 /// \returns Whether or not the allocator object is active.
240 /// \note The active allocator object is the last object created for one stack.
241 /// Moving changes the active allocator.
242 bool is_active() const noexcept;
243
244 /// \effects Instructs it to release unnecessary memory after automatic unwinding occurs.
245 /// This will effectively forward to \ref memory_stack::shrink_to_fit() of the internal stack.
246 /// \note Like the use of the \ref temporary_stack_initializer this can be used as an optimization,
247 /// to tell when the thread's \ref temporary_stack isn't needed anymore and can be destroyed.
248 /// \note It doesn't call shrink to fit immediately, only in the destructor!
249 void shrink_to_fit() noexcept;
250
251 /// \returns The internal stack the temporary allocator is using.
252 /// \requires `is_active()` must return `true`.
253 temporary_stack& get_stack() const noexcept
254 {
255 return unwind_.get_stack();
256 }
257
258 private:
260 temporary_allocator* prev_;
261 bool shrink_to_fit_;
262 };
263
264 template <class Allocator>
265 class allocator_traits;
266
267 /// Specialization of the \ref allocator_traits for \ref temporary_allocator classes.
268 /// \note It is not allowed to mix calls through the specialization and through the member functions,
269 /// i.e. \ref temporary_allocator::allocate() and this \c allocate_node().
270 /// \ingroup memory_allocator
271 template <>
273 {
274 public:
276 using is_stateful = std::true_type;
277
278 /// \returns The result of \ref temporary_allocator::allocate().
279 static void* allocate_node(allocator_type& state, std::size_t size,
280 std::size_t alignment)
281 {
282 detail::check_allocation_size<bad_node_size>(size,
283 [&] { return max_node_size(state); },
285 "::temporary_allocator",
286 &state});
287 return state.allocate(size, alignment);
288 }
289
290 /// \returns The result of \ref temporary_allocator::allocate().
291 static void* allocate_array(allocator_type& state, std::size_t count, std::size_t size,
292 std::size_t alignment)
293 {
294 return allocate_node(state, count * size, alignment);
295 }
296
297 /// @{
298 /// \effects Does nothing besides bookmarking for leak checking, if that is enabled.
299 /// Actual deallocation will be done automatically if the allocator object goes out of scope.
300 static void deallocate_node(const allocator_type&, void*, std::size_t,
301 std::size_t) noexcept
302 {
303 }
304
305 static void deallocate_array(const allocator_type&, void*, std::size_t, std::size_t,
306 std::size_t) noexcept
307 {
308 }
309 /// @}
310
311 /// @{
312 /// \returns The maximum size which is \ref memory_stack::next_capacity() of the internal stack.
313 static std::size_t max_node_size(const allocator_type& state) noexcept
314 {
315 return state.get_stack().next_capacity();
316 }
317
318 static std::size_t max_array_size(const allocator_type& state) noexcept
319 {
320 return max_node_size(state);
321 }
322 /// @}
323
324 /// \returns The maximum possible value since there is no alignment restriction
325 /// (except indirectly through \ref memory_stack::next_capacity()).
326 static std::size_t max_alignment(const allocator_type&) noexcept
327 {
328 return std::size_t(-1);
329 }
330 };
331 } // namespace memory
332} // namespace wpi
333
334#endif // WPI_MEMORY_TEMPORARY_ALLOCATOR_HPP_INCLUDED
static std::size_t max_node_size(const allocator_type &state) noexcept
Definition temporary_allocator.hpp:313
static void * allocate_array(allocator_type &state, std::size_t count, std::size_t size, std::size_t alignment)
Definition temporary_allocator.hpp:291
std::true_type is_stateful
Definition temporary_allocator.hpp:276
static void * allocate_node(allocator_type &state, std::size_t size, std::size_t alignment)
Definition temporary_allocator.hpp:279
static std::size_t max_array_size(const allocator_type &state) noexcept
Definition temporary_allocator.hpp:318
static void deallocate_array(const allocator_type &, void *, std::size_t, std::size_t, std::size_t) noexcept
Definition temporary_allocator.hpp:305
static void deallocate_node(const allocator_type &, void *, std::size_t, std::size_t) noexcept
Definition temporary_allocator.hpp:300
static std::size_t max_alignment(const allocator_type &) noexcept
Definition temporary_allocator.hpp:326
The default specialization of the allocator_traits for a RawAllocator.
Definition allocator_traits.hpp:292
Definition temporary_allocator.hpp:27
std::size_t next_block_size() const noexcept
Definition temporary_allocator.hpp:35
void(*)(std::size_t size) growth_tracker
Definition temporary_allocator.hpp:40
temporary_block_allocator(std::size_t block_size) noexcept
growth_tracker set_growth_tracker(growth_tracker t) noexcept
growth_tracker get_growth_tracker() noexcept
Definition temporary_allocator.hpp:81
temporary_stack_list_node(int) noexcept
Definition temporary_allocator.hpp:85
~temporary_stack_list_node() noexcept
Definition temporary_allocator.hpp:87
temporary_stack_list_node() noexcept
Definition temporary_allocator.hpp:83
Simple utility that automatically unwinds a Stack to a previously saved location.
Definition memory_stack.hpp:282
Alias template for an STL container that uses a certain RawAllocator.
Definition container.hpp:184
A stateful RawAllocator that handles temporary allocations.
Definition temporary_allocator.hpp:220
temporary_allocator(temporary_stack &stack)
Manually takes care of the lifetime of the per-thread temporary_stack.
Definition temporary_allocator.hpp:174
temporary_stack_initializer(defer_create_t) noexcept
Definition temporary_allocator.hpp:186
temporary_stack_initializer(std::size_t initial_size=default_stack_size)
A wrapper around the memory_stack that is used by the temporary_allocator.
Definition temporary_allocator.hpp:96
growth_tracker get_growth_tracker() noexcept
Definition temporary_allocator.hpp:115
temporary_stack(std::size_t initial_size)
Definition temporary_allocator.hpp:123
growth_tracker set_growth_tracker(growth_tracker t) noexcept
Definition temporary_allocator.hpp:109
detail::temporary_block_allocator::growth_tracker growth_tracker
The type of the handler called when the internal memory_stack grows.
Definition temporary_allocator.hpp:103
std::size_t next_capacity() const noexcept
Definition temporary_allocator.hpp:128
Configuration macros.
#define WPI_MEMORY_LOG_PREFIX
Definition config.hpp:46
Class wpi::memory::memory_stack and its wpi::memory::allocator_traits specialization.
detail namespace with internal helper functions
Definition input_adapters.h:32
Implement std::hash so that hash_code can be used in STL containers.
Definition PointerIntPair.h:280
Memory namespace.
Definition heap_allocator.hpp:20
Foonathan namespace.
Definition ntcore_cpp.h:26
A memory block.
Definition memory_arena.hpp:28
Definition temporary_allocator.hpp:179
defer_create_t() noexcept
Definition temporary_allocator.hpp:180