I made a statically allocated memory pool for embedded systems. I think it needs a little work but it works fine so far.
How it works: An array of size MEMORY_POOL_SIZE
is first reserved in memory and that's actually the space and the whole program uses to get memory from.
The number of allocations that can happen at the same time are limited and defined from MEMORY_POOLS_NUM
. The current implementation below uses 16384 bytes
of memory space and 64
concurrent memory allocations. The numbers don't have to be power of 2.
When the program wants to allocate some memory, it access the memory allocator which passes a pointer back to the program.
It is thread safe, which means the program is not interrupted during the allocation or de-allocation of the memory pool.
When the size an allocated space needs to change (realloc) everything in memory has to move. When the pointers of the mem_allocator
point to a different address, the program will break and for this reason the pointers that are passed back to the program point to the address of the
start_p
.
-- mem_pool.h --
#ifndef MEM_POOL_H
#define MEM_POOL_H
#include <inttypes.h>
/**
* \brief Allocates a chunk from the memory pool
* \param buf Must be a double NULL pointer.
* \param size Must be greater than zero.
*/
void mem_pool_alloc(uint8_t ** buf, uint16_t size);
/**
* \brief Re-allocates a chunk from the memory pool.
* The new chunk is always bigger.
* \param buf Must be a double NULL pointer.
* \param size Must be greater than zero.
*/
void mem_pool_realloc(uint8_t ** buf, uint16_t new_size);
/**
* \brief Release a chunk from the memory pool.
* \param buf Must be a double pointer.
*/
void mem_pool_free(uint8_t ** buf);
#endif
-- mem_pool.c --
#include "mem_pool.h"
#include <stddef.h>
#include <stdbool.h>
#define MEMORY_POOL_SIZE (16384) /** The size in bytes of the statically allocated memory */
#define MEMORY_POOLS_NUM (64) /** The number of elements of the memory allocator */
typedef struct
{
bool locked; /** Shows whether the element of the memory allocator is reserved or not */
uint8_t* start_p; /** The pointer to the first element in the statically allocated memory */
uint16_t size; /** The total size of the chunk that is allocated from the memory */
}MemPoolAllocator;
/**
* The statically allocated array in memory.
*/
static volatile uint8_t memory_pool[MEMORY_POOL_SIZE];
/**
* Memory allocator struct. It is used to allocate memory in the pool.
*/
static volatile MemPoolAllocator mem_allocator[MEMORY_POOLS_NUM];
void mem_pool_alloc(uint8_t ** buf, uint16_t size)
{
ASSERT(buf != NULL);
ASSERT(*buf == NULL);
ASSERT(size > 0);
__disable_irq();
for (uint8_t i = 0; i < MEMORY_POOLS_NUM; i++) {
if (mem_allocator[i].locked == false) {
mem_allocator[i].locked = true;
// Find the first unlocked element.
if (i > 0) {
mem_allocator[i].start_p = mem_allocator[i - 1].start_p + mem_allocator[i - 1].size;
}
else {
// If the element is the first one the pointer of the chunk points to the first byte of
// the statically allocated memory.
mem_allocator[i].start_p = memory_pool;
}
mem_allocator[i].size = size;
// The double pointer points to the address of the pointer.
// This allows the pointers that the program use to be updated in case
// a re-allocation is needed.
*buf = &mem_allocator[i].start_p;
__enable_irq();
return;
}
}
*buf = NULL;
__enable_irq();
}
void mem_pool_realloc(uint8_t ** buf, uint16_t new_size)
{
ASSERT(buf != NULL);
ASSERT(new_size > 0);
__disable_irq();
if (*buf == NULL) {
mem_pool_alloc(buf, new_size);
}
else {
for (uint8_t id = 0; id < MEMORY_POOLS_NUM; id++) {
if (*buf == &mem_allocator[id].start_p) {
// The current element must be locked.
ASSERT(mem_allocator[id].locked == true);
// Starting from the end, all elements of the memory pool and
// the memory allocator must move as many as the new_size variable.
for (uint8_t x = MEMORY_POOLS_NUM - 1; x > id; x--) {
if (mem_allocator[x].locked == true) {
uint16_t temp_size = mem_allocator[x].size;
uint16_t offset_size = new_size - mem_allocator[x].size;
for (uint16_t a = mem_allocator[x].size; a > 0; a--) {
*(mem_allocator[x].start_p + a + offset_size - 1) = *(mem_allocator[x].start_p + a - 1);
*(mem_allocator[x].start_p + a - 1) = 0; // Initialize the new space to 0.
}
mem_allocator[x].start_p += offset_size;
mem_allocator[x].size = new_size;
}
}
break;
}
}
}
__enable_irq();
}
void mem_pool_free(uint8_t ** buf)
{
if ((*buf == NULL) || (buf == NULL)) {
return;
}
__disable_irq();
uint8_t id = 0;
for (id = 0; id < MEMORY_POOLS_NUM; id++) {
if (*buf == &mem_allocator[id].start_p) {
ASSERT(mem_allocator[id].locked == true);
mem_allocator[id].locked = false;
*buf = NULL;
for (uint16_t i = 0; i < mem_allocator[id].size; i++) {
mem_allocator[id].start_p[i] = 0;
}
mem_allocator[id].start_p = NULL;
mem_allocator[id].size = 0;
break;
}
}
__enable_irq();
}