Memory Allocation with Fw::MemAllocator
This document treats the design pattern of how to allocate blocks of memory in F´ using dynamic (heap) memory allocation. This is restricted to system initialization only, as dynamic memory allocation during runtime is forbidden by flight software coding standards for safety and reliability reasons.
To learn more about working with buffers beyond system initialization in F´, please refer to the Buffer Pool Guide document.
Fw::MemAllocator Overview
The Fw::MemAllocator interface provides an abstraction for memory allocation to be used during system initialization. Components that require memory to be allocated at startup can use this interface to request memory blocks with sizes specified at runtime.
When to use Fw::MemAllocator
Components may need memory internally to function. There are several reasons why dynamic allocation through Fw::MemAllocator is used instead of global, static, stack or component member variables:
-
Size constraints: Certain buffers are too large for stack allocation. Stack sizes are typically limited and large allocations can cause stack overflow.
-
Runtime sizing: The size of the memory required may not be known at compile time. This includes cases where multiple instances of a component may require different sizes of memory.
A good example is the Svc::BufferManager component, which manages pools of buffers of configurable sizes. These pools are often large and the number can vary widely based on project requirements. There can also be the need to have multiple instances of the Svc::BufferManager, each instance with different size configurations. Using Fw::MemAllocator allows each instance of Svc::BufferManager to independently allocate the necessary memory during system initialization.
Tip
Many other components in F´ also use Fw::MemAllocator, you can search the codebase for MemAllocator to find more examples.
The core framework ships with an implementation of Fw::MemAllocator, called Fw::MallocAllocator, which delegates to the C/C++ malloc() and free() functions. Projects are free to implement their own versions of Fw::MemAllocator if desired.
Warning
Flight Software coding standards forbid dynamic memory allocation outside of system initialization. This is for safety and reliability reasons. Therefore, the use of Fw::MemAllocator is intended for use during initialization only, typically through a component configure()/setup() method called during configureTopology(). For runtime memory management during operation, please consult the Buffer Pools with Svc.BufferManager document.
Pattern to use the Fw::MemAllocator
The following steps outline the design pattern for using Fw::MemAllocator in a component to allocate memory during system initialization:
-
Define a Member Variable for the Allocator: In your component class, define a member variable to hold a pointer to the
Fw::MemAllocator. It will be used to deallocate memory during shutdown. -
Specify (or Modify) the Setup Method: Update or create a setup method for your component, usually called
configure()/setup(). This method should accept a reference to anFw::MemAllocator. Use this allocator to allocate the required memory blocks in the setup method.void MyComponent::setup(Fw::MemAllocator& memAllocator, FwSizeType memorySize, FwEnumStoreType memId /* other params */) { this->m_memAllocator = &memAllocator; // Store the allocator for later use this->m_memSize = memorySize; // Store the requested memory size // Allocate memory using the provided arguments this->m_memPtr = this->m_memAllocator->allocate(memId, this->m_memSize); if (this->m_memPtr == nullptr) { // Handle allocation failure } } // [... component code can use the allocated memory after setup() has been called in Topology.cpp ...]The
memIdparameter is the memory segment identifier and should be unique per component. The usage of this identifier is specific to each implementation ofFw::MemAllocator. TheFw::MallocAllocatorimplementation does not use this value.Important
The
Fw::MemAllocatorinstance's lifespan must persist at least through thecleanup()method call (or until the component's destructor ifcleanup()is not called), as it will be used for deallocation. -
Deallocate Memory During Shutdown: In the component's cleanup method, ensure that you deallocate any memory that was allocated using the
Fw::MemAllocator. Use the stored allocator pointer to free the memory. -
Update Topology Configuration and Teardown: In your deployment's
Topology.cpp(or phase code if you use phases), ensure to callsetup()andcleanup(), choosing an appropriateFw::MemAllocatorimplementation to pass in. For demonstration purposes, let's imagine that we need two instances ofMyComponent, each requiring different memory sizes:#include "Fw/MemAllocator/MemAllocator.hpp" static Fw::MallocAllocator mallocAllocator; // Here we choose to use the MallocAllocator implementation void configureTopology() { // [... other component initializations ...] // Setup two MyComponent instances with a required memory size of 1024 and 2048 bytes respectively // Pass unique IDs for memory tracking myComponentInstanceOne.setup(mallocAllocator, 1024, 100); myComponentInstanceTwo.setup(mallocAllocator, 2048, 101); } void teardownTopology(const TopologyState& state) {\ // Cleanup MyComponent to deallocate memory myComponentInstanceOne.cleanup(); myComponentInstanceTwo.cleanup(); // [... other component cleanups ...] }This assumes that your
instances.fppwould have defined both instances like the following:
Once the memory is allocated in the setup() method, the component can use the allocated memory as needed until shutdown. The memory will be properly deallocated in the cleanup() method when the component is shut down. To see examples of this pattern in practice, refer to components like Svc::BufferManager, Svc::FrameAccumulator, and others, in the F´ codebase.
Fw::MemAllocatorRegistry
The Fw::MemAllocatorRegistry is a singleton registry that allows registering different memory allocators for different purposes within the system. This is useful when projects want to manage multiple types of memory allocators in a centralized way. Projects may define project-specific allocators that implement the Fw::MemAllocator interface and register them with the registry.
Basic Usage
// In your topology initialization:
#include <Fw/Types/MemAllocator.hpp>
// Get the singleton registry
Fw::MemAllocatorRegistry& registry = Fw::MemAllocatorRegistry::getInstance();
// Instantiate and register custom allocators for different purposes
Fw::MallocAllocator systemAllocator;
MyCustomAllocator specialPurposeAllocator;
registry.registerAllocator(Fw::MemoryAllocation::MemoryAllocatorType::SYSTEM, systemAllocator);
registry.registerAllocator(Fw::MemoryAllocation::MemoryAllocatorType::CUSTOM_ALLOCATOR_1, specialPurposeAllocator);
// [... other code ...]
// Later, retrieve and use allocators
Fw::MemAllocator& allocator = registry.getAllocator(Fw::MemoryAllocation::MemoryAllocatorType::SYSTEM);
Note
The registry maintains pointers to allocators; it does not take ownership. Registered allocators must remain valid for the duration of the program.
The types of allocators available are defined in config/MemoryAllocation.fpp and can be customized per deployment.