pw_allocator#
Flexible, safe, and measurable memory allocation
Unstable C++17 Code Size Impact: 400 to 1800 bytes
Flexible: Simple interface makes it easy to inject specific behaviors.
Safe: Can detect memory corruption, e.g overflows and use-after-free.
Measurable: Pick what allocations you want to track and measure.
1using pw::allocator::Layout;
2
3void* AllocateNamedU32(pw::Allocator& allocator) {
4 return allocator.Allocate(Layout::Of<NamedU32>());
5}
// Any of these allocators can be passed to the routine above.
WithBuffer<LastFitBlockAllocator<uint32_t>, 0x1000> block_allocator;
LibCAllocator libc_allocator;
TrackingAllocator tracker(libc_allocator);
SynchronizedAllocator synced(*block_allocator);
Dynamically allocate without giving up control! Pigweed’s allocators let you easily combine allocator features for your needs, without extra code size or performance penalties for those you don’t. Complex projects in particular can benefit from dynamic allocation through simplified code, improved memory usage, increased shared memory, and reduced large reservations.
Want to allocate objects from specific memory like SRAM or PSRAM?
Use dependency injection! Write your code to take Allocator parameters, and you can quickly and easily change where memory comes from or what additional features are provided simply by changing what allocator is passed:
1class NamedU32Factory {
2 public:
3 explicit NamedU32Factory(pw::Allocator& allocator) : allocator_(allocator) {}
4
5 auto MakeNamedU32(std::string_view name, uint32_t value) {
6 return allocator_.MakeUnique<NamedU32>(name, value);
7 }
8
9 private:
10 pw::Allocator& allocator_;
11};
Now you can easily allocate objects in the example above using SRAM, PSRAM, or both:
1// Set up an object that allocates from SRAM memory.
2PW_PLACE_IN_SECTION(".sram") std::array<std::byte, 0x1000> sram_buffer;
3using SramBlock = ::pw::allocator::FirstFitBlock<uint16_t>;
4pw::allocator::FirstFitAllocator<SramBlock> sram_allocator(sram_buffer);
5NamedU32Factory sram_factory(sram_allocator);
6
7// Set up an object that allocates from PSRAM memory.
8PW_PLACE_IN_SECTION(".psram") std::array<std::byte, 0x2000> psram_buffer;
9using PsramBlock = ::pw::allocator::WorstFitBlock<uint32_t>;
10pw::allocator::WorstFitAllocator<PsramBlock> psram_allocator(psram_buffer);
11NamedU32Factory psram_factory(psram_allocator);
Worried about forgetting to deallocate?
Use a smart pointer!
1pw::UniquePtr<NamedU32> MakeNamedU32(pw::Allocator& allocator,
2 std::string_view name,
3 uint32_t value) {
4 return allocator.MakeUnique<NamedU32>(name, value);
5}
Want to know how much memory has been allocated?
Pick the metrics you’re interested in and track them with a TrackingAllocator:
1struct CustomMetrics {
2 PW_ALLOCATOR_METRICS_ENABLE(allocated_bytes);
3 PW_ALLOCATOR_METRICS_ENABLE(peak_allocated_bytes);
4 PW_ALLOCATOR_METRICS_ENABLE(num_failures);
5};
1 constexpr pw::metric::Token kToken = PW_TOKENIZE_STRING("CustomMetrics");
2 pw::allocator::TrackingAllocatorImpl<CustomMetrics> tracker(kToken,
3 allocator);
Need to share the allocator with another thread or an interrupt handler?
Use a SynchronizedAllocator with the lock of your choice:
1void RunTasks(pw::Allocator& allocator, const pw::thread::Options& options) {
2 pw::allocator::SynchronizedAllocator<pw::sync::InterruptSpinLock> synced(
3 allocator);
4 MyTask task1(synced, options);
5 MyTask task2(synced, options);
6 task1.join();
7 task2.join();
8}
Tip
Check out the Guides for even more code samples!
Is it right for you?#
Does your project need to use memory…
Without knowing exactly how much ahead of time?
From different backing storage, e.g. SRAM vs. PSRAM?
Across different tasks using a shared pool?
In multiple places, and you need to know how much each place is using?
If you answered “yes” to any of these questions, pw_allocator
may be able to
help! This module is designed to faciliate dynamic allocation for embedded
projects that are sufficiently complex to make static allocation infeasible.
Smaller projects may be able to enumerate their objects and preallocate any
storage they may need on device when running, and may be subject to extremely
tight memory constraints. In these cases, pw_allocator
may add more costs in
terms of code size, overhead, and performance than it provides benefits in terms
of code simplicity and memory sharing.
At the other extreme, larger projects may be built on platforms with rich
operating system capabilities. On these platforms, the system and language
runtime may already provide dynamic allocation and memory may be less
constrained. In these cases, pw_allocator
may not provide the same
capabilities as the platform.
Between these two is a range of complex projects on RTOSes and other platforms. These projects may benefit from using the Allocator interface and its implementations to manage memory.