WPILibC++ 2025.1.1
Loading...
Searching...
No Matches
tracking.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_TRACKING_HPP_INCLUDED
5#define WPI_MEMORY_TRACKING_HPP_INCLUDED
6
7/// \file
8/// Class \ref wpi::memory::tracked_allocator and related classes and functions.
9
10#include "detail/utility.hpp"
11#include "allocator_traits.hpp"
12#include "memory_arena.hpp"
13
14namespace wpi
15{
16 namespace memory
17 {
18 namespace detail
19 {
20 template <class Allocator, class Tracker>
21 auto set_tracker(int, Allocator& allocator, Tracker* tracker) noexcept
22 -> decltype(allocator.get_allocator().set_tracker(tracker))
23 {
24 return allocator.get_allocator().set_tracker(tracker);
25 }
26 template <class Allocator, class Tracker>
27 void set_tracker(short, Allocator&, Tracker*) noexcept
28 {
29 }
30
31 // used with deeply_tracked_allocator
32 template <class Tracker, class BlockAllocator>
33 class deeply_tracked_block_allocator : WPI_EBO(BlockAllocator)
34 {
35 public:
36 template <typename... Args>
37 deeply_tracked_block_allocator(std::size_t block_size, Args&&... args)
38 : BlockAllocator(block_size, detail::forward<Args>(args)...), tracker_(nullptr)
39 {
40 }
41
43 {
44 auto block = BlockAllocator::allocate_block();
45 if (tracker_) // on first call tracker_ is nullptr
46 tracker_->on_allocator_growth(block.memory, block.size);
47 return block;
48 }
49
50 void deallocate_block(memory_block block) noexcept
51 {
52 if (tracker_) // on last call tracker_ is nullptr again
53 tracker_->on_allocator_shrinking(block.memory, block.size);
54 BlockAllocator::deallocate_block(block);
55 }
56
57 std::size_t next_block_size() const noexcept
58 {
59 return BlockAllocator::next_block_size();
60 }
61
62 void set_tracker(Tracker* tracker) noexcept
63 {
64 tracker_ = tracker;
65 }
66
67 private:
68 Tracker* tracker_;
69 };
70 } // namespace detail
71
72 /// A BlockAllocator adapter that tracks another allocator using a tracker.
73 /// It wraps another BlockAllocator and calls the tracker function before forwarding to it.
74 /// The class can then be used anywhere a BlockAllocator is required and the memory usage will be tracked.<br>
75 /// It will only call the <tt>on_allocator_growth()</tt> and <tt>on_allocator_shrinking()</tt> tracking functions,
76 /// since a BlockAllocator is normally used inside higher allocators only.
77 /// \ingroup memory_adapter
78 template <class Tracker, class BlockOrRawAllocator>
80 : WPI_EBO(Tracker, make_block_allocator_t<BlockOrRawAllocator>)
81 {
82 public:
84 using tracker = Tracker;
85
86 /// @{
87 /// \effects Creates it by giving it a tracker and the tracked RawAllocator.
88 /// It will embed both objects.
89 explicit tracked_block_allocator(tracker t = {}) noexcept : tracker(detail::move(t)) {}
90
95 /// @}
96
97 /// \effects Creates it in the form required by the concept.
98 /// The allocator will be constructed using \c block_size and \c args.
99 template <typename... Args>
100 tracked_block_allocator(std::size_t block_size, tracker t, Args&&... args)
101 : tracker(detail::move(t)), allocator_type(block_size, detail::forward<Args>(args)...)
102 {
103 }
104
105 /// \effects Calls <tt>Tracker::on_allocator_growth()</tt> after forwarding to the allocator.
106 /// \returns The block as the returned by the allocator.
108 {
109 auto block = allocator_type::allocate_block();
110 this->on_allocator_growth(block.memory, block.size);
111 return block;
112 }
113
114 /// \effects Calls <tt>Tracker::on_allocator_shrinking()</tt> and forwards to the allocator.
115 void deallocate_block(memory_block block) noexcept
116 {
117 this->on_allocator_shrinking(block.memory, block.size);
118 allocator_type::deallocate_block(block);
119 }
120
121 /// \returns The next block size as returned by the allocator.
122 std::size_t next_block_size() const noexcept
123 {
124 return allocator_type::next_block_size();
125 }
126
127 /// @{
128 /// \returns A (const) reference to the used allocator.
130 {
131 return *this;
132 }
133
134 const allocator_type& get_allocator() const noexcept
135 {
136 return *this;
137 }
138 /// @}
139
140 /// @{
141 /// \returns A (const) reference to the tracker.
143 {
144 return *this;
145 }
146
147 const tracker& get_tracker() const noexcept
148 {
149 return *this;
150 }
151 /// @}
152 };
153
154 /// Similar to \ref tracked_block_allocator, but shares the tracker with the higher level allocator.
155 /// This allows tracking both (de-)allocations and growth with one tracker.
156 /// \note Due to implementation reasons, it cannot track growth and shrinking in the constructor/destructor of the higher level allocator.
157 /// \ingroup memory_adapter
158 template <class Tracker, class BlockOrRawAllocator>
159 using deeply_tracked_block_allocator = WPI_IMPL_DEFINED(
162
163 /// A RawAllocator adapter that tracks another allocator using a tracker.
164 /// It wraps another RawAllocator and calls the tracker function before forwarding to it.
165 /// The class can then be used anywhere a RawAllocator is required and the memory usage will be tracked.<br>
166 /// If the RawAllocator uses \ref deeply_tracked_block_allocator as BlockAllocator,
167 /// it will also track growth and shrinking of the allocator.
168 /// \ingroup memory_adapter
169 template <class Tracker, class RawAllocator>
171 : WPI_EBO(Tracker, allocator_traits<RawAllocator>::allocator_type)
172 {
175
176 public:
178 using tracker = Tracker;
179
180 using is_stateful = std::integral_constant<bool, traits::is_stateful::value
181 || !std::is_empty<Tracker>::value>;
182
183 /// @{
184 /// \effects Creates it by giving it a tracker and the tracked RawAllocator.
185 /// It will embed both objects.
186 /// \note This will never call the <tt>Tracker::on_allocator_growth()</tt> function.
187 explicit tracked_allocator(tracker t = {}) noexcept
189 {
190 }
191
197 /// @}
198
199 /// \effects Destroys both tracker and allocator.
200 /// \note This will never call the <tt>Tracker::on_allocator_shrinking()</tt> function.
202 {
203 detail::set_tracker(0, get_allocator(), static_cast<tracker*>(nullptr));
204 }
205
206 /// @{
207 /// \effects Moving moves both the tracker and the allocator.
213
215 {
216 tracker:: operator=(detail::move(other));
217 allocator_type::operator=(detail::move(other));
219 return *this;
220 }
221 /// @}
222
223 /// \effects Calls <tt>Tracker::on_node_allocation()</tt> and forwards to the allocator.
224 /// If a growth occurs and the allocator is deeply tracked, also calls <tt>Tracker::on_allocator_growth()</tt>.
225 /// \returns The result of <tt>allocate_node()</tt>
226 void* allocate_node(std::size_t size, std::size_t alignment)
227 {
228 auto mem = traits::allocate_node(get_allocator(), size, alignment);
229 this->on_node_allocation(mem, size, alignment);
230 return mem;
231 }
232
233 /// \effects Calls the composable node allocation function.
234 /// If allocation was successful, also calls `Tracker::on_node_allocation()`.
235 /// \returns The result of `try_allocate_node()`.
236 void* try_allocate_node(std::size_t size, std::size_t alignment) noexcept
237 {
238 auto mem = composable_traits::try_allocate_node(get_allocator(), size, alignment);
239 if (mem)
240 this->on_node_allocation(mem, size, alignment);
241 return mem;
242 }
243
244 /// \effects Calls <tt>Tracker::on_array_allocation()</tt> and forwards to the allocator.
245 /// If a growth occurs and the allocator is deeply tracked, also calls <tt>Tracker::on_allocator_growth()</tt>.
246 /// \returns The result of <tt>allocate_array()</tt>
247 void* allocate_array(std::size_t count, std::size_t size, std::size_t alignment)
248 {
249 auto mem = traits::allocate_array(get_allocator(), count, size, alignment);
250 this->on_array_allocation(mem, count, size, alignment);
251 return mem;
252 }
253
254 /// \effects Calls the composable array allocation function.
255 /// If allocation was succesful, also calls `Tracker::on_array_allocation()`.
256 /// \returns The result of `try_allocate_array()`.
257 void* try_allocate_array(std::size_t count, std::size_t size,
258 std::size_t alignment) noexcept
259 {
260 auto mem =
261 composable_traits::try_allocate_array(get_allocator(), count, size, alignment);
262 if (mem)
263 this->on_array_allocation(mem, count, size, alignment);
264 return mem;
265 }
266
267 /// \effects Calls <tt>Tracker::on_node_deallocation()</tt> and forwards to the allocator's <tt>deallocate_node()</tt>.
268 /// If shrinking occurs and the allocator is deeply tracked, also calls <tt>Tracker::on_allocator_shrinking()</tt>.
269 void deallocate_node(void* ptr, std::size_t size, std::size_t alignment) noexcept
270 {
271 this->on_node_deallocation(ptr, size, alignment);
272 traits::deallocate_node(get_allocator(), ptr, size, alignment);
273 }
274
275 /// \effects Calls the composable node deallocation function.
276 /// If it was succesful, also calls `Tracker::on_node_deallocation()`.
277 /// \returns The result of `try_deallocate_node()`.
278 bool try_deallocate_node(void* ptr, std::size_t size, std::size_t alignment) noexcept
279 {
280 auto res =
282 if (res)
283 this->on_node_deallocation(ptr, size, alignment);
284 return res;
285 }
286
287 /// \effects Calls <tt>Tracker::on_array_deallocation()</tt> and forwards to the allocator's <tt>deallocate_array()</tt>.
288 /// If shrinking occurs and the allocator is deeply tracked, also calls <tt>Tracker::on_allocator_shrinking()</tt>.
289 void deallocate_array(void* ptr, std::size_t count, std::size_t size,
290 std::size_t alignment) noexcept
291 {
292 this->on_array_deallocation(ptr, count, size, alignment);
293 traits::deallocate_array(get_allocator(), ptr, count, size, alignment);
294 }
295
296 /// \effects Calls the composable array deallocation function.
297 /// If it was succesful, also calls `Tracker::on_array_deallocation()`.
298 /// \returns The result of `try_deallocate_array()`.
299 bool try_deallocate_array(void* ptr, std::size_t count, std::size_t size,
300 std::size_t alignment) noexcept
301 {
302 auto res = composable_traits::try_deallocate_array(ptr, count, size, alignment);
303 if (res)
304 this->on_array_deallocation(ptr, count, size, alignment);
305 return res;
306 }
307
308 /// @{
309 /// \returns The result of the corresponding function on the wrapped allocator.
310 std::size_t max_node_size() const
311 {
313 }
314
315 std::size_t max_array_size() const
316 {
318 }
319
320 std::size_t max_alignment() const
321 {
323 }
324 /// @}
325
326 /// @{
327 /// \returns A (\c const) reference to the wrapped allocator.
329 {
330 return *this;
331 }
332
333 const allocator_type& get_allocator() const noexcept
334 {
335 return *this;
336 }
337 /// @}
338
339 /// @{
340 /// \returns A (\c const) reference to the tracker.
342 {
343 return *this;
344 }
345
346 const tracker& get_tracker() const noexcept
347 {
348 return *this;
349 }
350 /// @}
351 };
352
353 /// \effects Takes a RawAllocator and wraps it with a tracker.
354 /// \returns A \ref tracked_allocator with the corresponding parameters forwarded to the constructor.
355 /// \relates tracked_allocator
356 template <class Tracker, class RawAllocator>
365
366 namespace detail
367 {
368 template <typename T, bool Block>
369 struct is_block_or_raw_allocator_impl : std::true_type
370 {
371 };
372
373 template <typename T>
377
378 template <typename T>
380 : is_block_or_raw_allocator_impl<T, memory::is_block_allocator<T>::value>
381 {
382 };
383
384 template <class RawAllocator, class BlockAllocator>
386
387 template <template <typename...> class RawAllocator, typename... Args,
388 class OtherBlockAllocator>
389 struct rebind_block_allocator<RawAllocator<Args...>, OtherBlockAllocator>
390 {
391 using type =
392 RawAllocator<typename std::conditional<is_block_or_raw_allocator<Args>::value,
393 OtherBlockAllocator, Args>::type...>;
394 };
395
396 template <class Tracker, class RawAllocator>
399 typename RawAllocator::allocator_type>;
400
401 template <class Tracker, class RawAllocator>
404 } // namespace detail
405
406 /// A \ref tracked_allocator that has rebound any BlockAllocator to the corresponding \ref deeply_tracked_block_allocator.
407 /// This makes it a deeply tracked allocator.<br>
408 /// It replaces each template argument of the given RawAllocator for which \ref is_block_allocator or \ref is_raw_allocator is \c true with a \ref deeply_tracked_block_allocator.
409 /// \ingroup memory_adapter
410 template <class Tracker, class RawAllocator>
414
415 /// \effects Takes a RawAllocator and deeply wraps it with a tracker.
416 /// \returns A \ref deeply_tracked_allocator with the corresponding parameters forwarded to the constructor.
417 /// \relates deeply_tracked_allocator
418 template <class RawAllocator, class Tracker, typename... Args>
426 } // namespace memory
427} // namespace wpi
428
429#endif // WPI_MEMORY_TRACKING_HPP_INCLUDED
The default specialization of the wpi::memory::allocator_traits.
The default specialization of the allocator_traits for a RawAllocator.
Definition allocator_traits.hpp:292
static void deallocate_array(allocator_type &state, void *array, std::size_t count, std::size_t size, std::size_t alignment) noexcept
Definition allocator_traits.hpp:328
traits_detail::allocator_type< Allocator > allocator_type
Definition allocator_traits.hpp:294
static void * allocate_array(allocator_type &state, std::size_t count, std::size_t size, std::size_t alignment)
Definition allocator_traits.hpp:308
static std::size_t max_array_size(const allocator_type &state)
Definition allocator_traits.hpp:346
static std::size_t max_node_size(const allocator_type &state)
Definition allocator_traits.hpp:338
static std::size_t max_alignment(const allocator_type &state)
Definition allocator_traits.hpp:354
static void * allocate_node(allocator_type &state, std::size_t size, std::size_t alignment)
Definition allocator_traits.hpp:298
static void deallocate_node(allocator_type &state, void *node, std::size_t size, std::size_t alignment) noexcept
Definition allocator_traits.hpp:318
The default specialization of the composable_allocator_traits for a ComposableAllocator.
Definition allocator_traits.hpp:500
static void * try_allocate_node(allocator_type &state, std::size_t size, std::size_t alignment) noexcept
Definition allocator_traits.hpp:504
static void * try_allocate_array(allocator_type &state, std::size_t count, std::size_t size, std::size_t alignment) noexcept
Definition allocator_traits.hpp:513
static bool try_deallocate_node(allocator_type &state, void *node, std::size_t size, std::size_t alignment) noexcept
Definition allocator_traits.hpp:522
static bool try_deallocate_array(allocator_type &state, void *array, std::size_t count, std::size_t size, std::size_t alignment) noexcept
Definition allocator_traits.hpp:531
A tracked_allocator that has rebound any BlockAllocator to the corresponding deeply_tracked_block_all...
Definition tracking.hpp:413
auto make_deeply_tracked_allocator(Tracker t, Args &&... args) -> deeply_tracked_allocator< Tracker, RawAllocator >
Definition tracking.hpp:419
deeply_tracked_block_allocator(std::size_t block_size, Args &&... args)
Definition tracking.hpp:37
void set_tracker(Tracker *tracker) noexcept
Definition tracking.hpp:62
std::size_t next_block_size() const noexcept
Definition tracking.hpp:57
memory_block allocate_block()
Definition tracking.hpp:42
void deallocate_block(memory_block block) noexcept
Definition tracking.hpp:50
A RawAllocator adapter that tracks another allocator using a tracker.
Definition tracking.hpp:172
void deallocate_node(void *ptr, std::size_t size, std::size_t alignment) noexcept
Definition tracking.hpp:269
tracker & get_tracker() noexcept
Definition tracking.hpp:341
bool try_deallocate_node(void *ptr, std::size_t size, std::size_t alignment) noexcept
Definition tracking.hpp:278
void * allocate_array(std::size_t count, std::size_t size, std::size_t alignment)
Definition tracking.hpp:247
void * try_allocate_array(std::size_t count, std::size_t size, std::size_t alignment) noexcept
Definition tracking.hpp:257
bool try_deallocate_array(void *ptr, std::size_t count, std::size_t size, std::size_t alignment) noexcept
Definition tracking.hpp:299
tracked_allocator(tracker t, allocator_type &&allocator) noexcept
Definition tracking.hpp:192
allocator_type & get_allocator() noexcept
Definition tracking.hpp:328
void * try_allocate_node(std::size_t size, std::size_t alignment) noexcept
Definition tracking.hpp:236
const allocator_type & get_allocator() const noexcept
Definition tracking.hpp:333
~tracked_allocator() noexcept
Definition tracking.hpp:201
const tracker & get_tracker() const noexcept
Definition tracking.hpp:346
void * allocate_node(std::size_t size, std::size_t alignment)
Definition tracking.hpp:226
tracked_allocator(tracker t={}) noexcept
Definition tracking.hpp:187
std::size_t max_node_size() const
Definition tracking.hpp:310
std::size_t max_alignment() const
Definition tracking.hpp:320
tracked_allocator(tracked_allocator &&other) noexcept
Definition tracking.hpp:208
typename allocator_traits< RawAllocator >::allocator_type allocator_type
Definition tracking.hpp:177
auto make_tracked_allocator(Tracker t, RawAllocator &&alloc) -> tracked_allocator< Tracker, typename std::decay< RawAllocator >::type >
Definition tracking.hpp:357
std::size_t max_array_size() const
Definition tracking.hpp:315
Tracker tracker
Definition tracking.hpp:178
std::integral_constant< bool, traits::is_stateful::value||!std::is_empty< Tracker >::value > is_stateful
Definition tracking.hpp:180
tracked_allocator & operator=(tracked_allocator &&other) noexcept
Definition tracking.hpp:214
void deallocate_array(void *ptr, std::size_t count, std::size_t size, std::size_t alignment) noexcept
Definition tracking.hpp:289
A BlockAllocator adapter that tracks another allocator using a tracker.
Definition tracking.hpp:81
std::size_t next_block_size() const noexcept
Definition tracking.hpp:122
tracked_block_allocator(std::size_t block_size, tracker t, Args &&... args)
Definition tracking.hpp:100
allocator_type & get_allocator() noexcept
Definition tracking.hpp:129
memory_block allocate_block()
Definition tracking.hpp:107
tracker & get_tracker() noexcept
Definition tracking.hpp:142
make_block_allocator_t< BlockOrRawAllocator > allocator_type
Definition tracking.hpp:83
const tracker & get_tracker() const noexcept
Definition tracking.hpp:147
void deallocate_block(memory_block block) noexcept
Definition tracking.hpp:115
Tracker tracker
Definition tracking.hpp:84
const allocator_type & get_allocator() const noexcept
Definition tracking.hpp:134
tracked_block_allocator(tracker t={}) noexcept
Definition tracking.hpp:89
tracked_block_allocator(tracker t, allocator_type &&alloc) noexcept
Definition tracking.hpp:91
#define WPI_ALIAS_TEMPLATE(Name,...)
Definition config.hpp:73
auto ptr(T p) -> const void *
Converts p to const void* for pointer formatting.
Definition format.h:3821
implementation_defined deeply_tracked_block_allocator
Similar to tracked_block_allocator, but shares the tracker with the higher level allocator.
Definition tracking.hpp:159
implementation_defined make_block_allocator_t
Takes either a BlockAllocator or a RawAllocator.
Definition memory_arena.hpp:622
Class wpi::memory::memory_arena and related functionality regarding BlockAllocators.
detail namespace with internal helper functions
Definition input_adapters.h:32
typename rebind_block_allocator< RawAllocator, deeply_tracked_block_allocator_for< Tracker, RawAllocator > >::type rebound_allocator
Definition tracking.hpp:402
T && forward(typename std::remove_reference< T >::type &t) noexcept
Definition utility.hpp:31
std::remove_reference< T >::type && move(T &&arg) noexcept
Definition utility.hpp:25
auto set_tracker(int, Allocator &allocator, Tracker *tracker) noexcept -> decltype(allocator.get_allocator().set_tracker(tracker))
Definition tracking.hpp:21
memory::deeply_tracked_block_allocator< Tracker, typename RawAllocator::allocator_type > deeply_tracked_block_allocator_for
Definition tracking.hpp:397
Memory namespace.
Definition heap_allocator.hpp:20
Foonathan namespace.
Definition ntcore_cpp.h:26
RawAllocator< typename std::conditional< is_block_or_raw_allocator< Args >::value, OtherBlockAllocator, Args >::type... > type
Definition tracking.hpp:391
Traits that check whether a type models concept RawAllocator.
Definition allocator_traits.hpp:418
A memory block.
Definition memory_arena.hpp:28