Appearance
Memory Pools
Fixed-size block allocator with optional blocking alloc.
What it is
A memory pool hands out N blocks of the same fixed size from a preallocated buffer. Unlike the general-purpose heap, allocation and release are O(1) (free-list pop / push) and never fragment — once the pool is created the worst-case behaviour is fully known.
When to use a pool instead of /
- Producer/consumer pipelines that recycle objects of identical shape (network packets, DMA buffers, dispatch messages).
- Code paths that must succeed deterministically or block until a slot is free, instead of failing on a fragmented heap.
- Builds with
configSUPPORT_DYNAMIC_ALLOCATION == 0— pools can run entirely on caller-provided storage, so the whole feature works without a heap at all (see Example 2).
Semantics at a glance
osMemoryPoolAllocis blocking with timeout. It waits on a counting semaphore for a free slot, so when the pool is empty the calling task sleeps instead of spinning or returningNULLimmediately. Pass0for non-blocking,osWaitForeverto wait indefinitely. ReturnsNULLonly on timeout or invalid input.- Block addresses are aligned to
sizeof(void*); the requestedblock_sizeis rounded up to that alignment internally, so osMemoryPoolGetBlockSize may report a value larger than what you passed to osMemoryPoolNew. osMemoryPoolNew,osMemoryPoolAlloc,osMemoryPoolFreeandosMemoryPoolDeletecannot be called from ISR context. The getters (GetCapacity/GetBlockSize/GetCount/GetSpace) may be called from an ISR; the snapshot they return is unlocked and therefore best-effort.osMemoryPoolDeletefails withosErrorResourceif any block is still checked out — the caller is responsible for returning all blocks first.
Example 1 — Dynamic pool (heap-backed)
Quickest to set up; control block and backing memory are allocated from the heap. Suitable when configSUPPORT_DYNAMIC_ALLOCATION is on and the pool is created during normal startup.
cpp
#include <kernel/cmsis_os2_ext.h>
typedef struct { uint8_t payload[64]; uint16_t len; } packet_t;
static osMemoryPoolId_t g_pkt_pool;
void packets_init(void) {
osMemoryPoolAttr_t a = { .name = "pkts" };
g_pkt_pool = osMemoryPoolNew(32, sizeof(packet_t), &a); // 32 blocks
}
void producer_task(void) {
packet_t* p = osMemoryPoolAlloc(g_pkt_pool, osWaitForever); // blocks
fill_packet(p);
enqueue_to_consumer(p);
}
void consumer_task(packet_t* p) {
handle(p);
osMemoryPoolFree(g_pkt_pool, p);
}Example 2 — Static pool (no heap required)
Provide both the control block storage (cb_mem) and the backing buffer (mp_mem). The pool will never call pvPortMalloc, so this works on a build with configSUPPORT_DYNAMIC_ALLOCATION == 0 and is the right choice for safety-critical paths where allocation failure at startup is unacceptable.
cpp
#define BLOCK_COUNT 16
#define BLOCK_SIZE 128
// Backing buffer must be at least BLOCK_COUNT * aligned(BLOCK_SIZE) bytes.
// Aligning the buffer itself avoids extra padding inside the pool.
static uint8_t s_pool_mem[BLOCK_COUNT * BLOCK_SIZE]
__attribute__((aligned(sizeof(void*))));
static uint8_t s_pool_cb[64]; // opaque; sized generously for the control block
static osMemoryPoolId_t g_static_pool;
void static_pool_init(void) {
osMemoryPoolAttr_t a = {
.name = "static",
.cb_mem = s_pool_cb, .cb_size = sizeof(s_pool_cb),
.mp_mem = s_pool_mem, .mp_size = sizeof(s_pool_mem),
};
g_static_pool = osMemoryPoolNew(BLOCK_COUNT, BLOCK_SIZE, &a);
}If cb_size is too small for the internal control block, or mp_size is smaller than block_count * aligned(block_size), osMemoryPoolNew returns NULL.
Types
| Name | |
|---|---|
| struct | osMemoryPoolAttr_t Optional attributes passed to osMemoryPoolNew. |
| typedef void * | osMemoryPoolId_t Opaque handle for a memory pool instance. |
Functions Overview
| Name | |
|---|---|
| osMemoryPoolId_t | osMemoryPoolNew(uint32_t block_count, uint32_t block_size, const osMemoryPoolAttr_t * attr) Creates a new fixed-size memory pool. |
| void * | osMemoryPoolAlloc(osMemoryPoolId_t mp_id, uint32_t timeout) Allocates one block from the pool, optionally blocking. |
| osStatus_t | osMemoryPoolFree(osMemoryPoolId_t mp_id, void * block) Returns a previously allocated block to the pool. |
| uint32_t | osMemoryPoolGetCapacity(osMemoryPoolId_t mp_id) Returns the total number of blocks the pool was created with. |
| uint32_t | osMemoryPoolGetBlockSize(osMemoryPoolId_t mp_id) Returns the actual block size in bytes (after alignment). |
| uint32_t | osMemoryPoolGetCount(osMemoryPoolId_t mp_id) Returns the number of blocks currently checked out (in use). |
| uint32_t | osMemoryPoolGetSpace(osMemoryPoolId_t mp_id) Returns the number of blocks currently free. |
| osStatus_t | osMemoryPoolDelete(osMemoryPoolId_t mp_id) Destroys a pool and releases any storage it owns. |
Types Documentation
typedef osMemoryPoolId_t
cpp
typedef void* osMemoryPoolId_t;Opaque handle for a memory pool instance.
Function Details
function osMemoryPoolNew
cpp
osMemoryPoolId_t osMemoryPoolNew(
uint32_t block_count,
uint32_t block_size,
const osMemoryPoolAttr_t * attr
)Creates a new fixed-size memory pool.
Parameters:
- block_count Number of blocks the pool will hold. Must be > 0.
- block_size Size of each block in bytes (will be aligned up). Must be > 0.
- attr Optional attributes; see osMemoryPoolAttr_t. May be
NULLfor an entirely heap-backed pool.
Return: Pool handle on success, NULL on failure (bad parameters, caller-supplied storage too small, allocation failure, or called from ISR).
Note: Must not be called from ISR context.
The internal block_size is rounded up to sizeof(void*) so the returned blocks are pointer-aligned. The pool maintains a free list plus a counting semaphore that drives the blocking behaviour of osMemoryPoolAlloc.
function osMemoryPoolAlloc
cpp
void * osMemoryPoolAlloc(
osMemoryPoolId_t mp_id,
uint32_t timeout
)Allocates one block from the pool, optionally blocking.
Parameters:
- mp_id Pool handle from osMemoryPoolNew.
- timeout Maximum wait in milliseconds. Use
0for a non-blocking try,osWaitForeverto wait indefinitely.
Return: Pointer to a block_size-sized block, or NULL on timeout, invalid handle, or call from ISR context.
Note: Must not be called from ISR context.
If a free block is available it is returned immediately. If the pool is empty the calling thread sleeps on the pool's counting semaphore until a block is released by another thread, or until timeout ticks elapse. The returned memory is uninitialised — treat it like the result of malloc.
function osMemoryPoolFree
cpp
osStatus_t osMemoryPoolFree(
osMemoryPoolId_t mp_id,
void * block
)Returns a previously allocated block to the pool.
Parameters:
- mp_id Pool handle.
- block Pointer previously returned by osMemoryPoolAlloc.
Returns:
- osOK Block successfully returned.
- osErrorParameter
mp_idorblockisNULL, orblockis not a valid block address for this pool. - osErrorISR Called from ISR context.
- osErrorResource Internal mutex could not be acquired.
The pointer must have come from osMemoryPoolAlloc on the same pool. The implementation validates that block lies inside the pool's backing buffer and is aligned on a block boundary; it does not detect double-free of a block that is currently free — make sure each Alloc is paired with exactly one Free.
function osMemoryPoolGetCapacity
cpp
uint32_t osMemoryPoolGetCapacity(
osMemoryPoolId_t mp_id
)Returns the total number of blocks the pool was created with.
function osMemoryPoolGetBlockSize
cpp
uint32_t osMemoryPoolGetBlockSize(
osMemoryPoolId_t mp_id
)Returns the actual block size in bytes (after alignment).
This may be larger than the block_size passed to osMemoryPoolNew because of the internal sizeof(void*) rounding.
function osMemoryPoolGetCount
cpp
uint32_t osMemoryPoolGetCount(
osMemoryPoolId_t mp_id
)Returns the number of blocks currently checked out (in use).
Safe to call from ISR context; the value is a best-effort unlocked snapshot in that case.
function osMemoryPoolGetSpace
cpp
uint32_t osMemoryPoolGetSpace(
osMemoryPoolId_t mp_id
)Returns the number of blocks currently free.
Equivalent to GetCapacity() - GetCount(). Safe to call from ISR context; the value is a best-effort unlocked snapshot in that case.
function osMemoryPoolDelete
cpp
osStatus_t osMemoryPoolDelete(
osMemoryPoolId_t mp_id
)Destroys a pool and releases any storage it owns.
Parameters:
- mp_id Pool handle.
Returns:
- osOK Pool deleted.
- osErrorParameter
mp_idisNULL. - osErrorISR Called from ISR context.
- osErrorResource One or more blocks are still in use.
Frees the control block and backing buffer only for fields that were originally allocated from the heap. Caller-supplied cb_mem / mp_mem are left untouched. All blocks must have been returned via osMemoryPoolFree before calling this.