nRF5 SDK v17.1.0
Memory object library

The memory object library allows allocation of variable length memory objects from the pool. Memory objects are built from fixed size linked memory blocks (chunks). Since data is fragmented, it cannot be directly accessed. Such approach allows effective memory utilization since size of a memory chunk and the number of chunks can be adjusted to the use case. Memory objects are designed to be shared between multiple users.

For API documentation of this module, see Memory Object module.

Implementation details

The memory object pool uses Block memory allocator to manage memory chunks. A memory object is built from multiple chunks allocated from Block memory allocator. A pointer to the first chunk in the memory object is used as a handle to that memory object.

Object

Each chunk consists of a header and data. A standard header has 4 bytes and contains a pointer to the next chunk in the object, or a pointer to the pool instance in case of the last chunk. The first chunk header has an extended header which additionally contains object details, including a usage counter and information about the number of chunks in the object.

Multiple users

Each user can claim (nrf_memobj_get) and release (nrf_memobj_put) an object. Claiming a buffer atomically increases the internal counter in the object (see Object). Releasing decrements the counter. The object is returned to the pool after the last release. Since the object has a pointer to the pool instance, it can be freed anywhere without access to the pool.

Working with objects

Before a memory object can be used, a pool has to be created and initialized. A pool is created using the NRF_MEMOBJ_POOL_DEF macro. Chunk size and pool size must be specified. Chunk size is the amount of data that can be stored in the standard chunk. Chunk size can be adjusted based on the usage to reduce fragmentation. Note that capacity of an object consisting of one chunk is reduced by 4 bytes because of the extended header.

NRF_MEMOBJ_POOL_DEF(m_pool, 16, 8); //Create pool of 8 objects, standard chunk size is 16 bytes
void foo(void)
{
err_code = nrf_memobj_pool_init(&m_pool);
//Pool can be used
}

If a memory object is used by a single user, nrf_memobj_alloc and nrf_memobj_free is used to allocate and free the object. Data is accessed using nrf_memobj_write and nrf_memobj_read. Reading and writing is done with an offset.

void foo(void)
{
nrf_memobj_t * p_obj = nrf_memobj_alloc(&m_pool, 33); //Allocate an object which can store 33 bytes
uint8_t buf1[] = {1,2};
uint8_t buf2[] = {3,4};
uint8_t outbuf[4];
size_t offset = 0;
nrf_memobj_write(p_obj, buf1, sizeof(buf1), offset);
offset += sizeof(buf1);
nrf_memobj_write(p_obj, buf2, sizeof(buf2), offset);
nrf_memobj_read(p_obj, outbuf, sizeof(buf1) + sizeof(buf2), 0);
}

When multiple modules use the buffer, nrf_memobj_get and nrf_memobj_put should be used. Do not use nrf_memobj_free in such case. The object is returned to the pool on the last nrf_memobj_put call.

void foo(void)
{
nrf_memobj_t * p_obj = nrf_memobj_alloc(&m_pool, 33); //Allocate an object which can store 33 bytes
send_to_foo1(p_obj);
send_to_foo2(p_obj);
}
void foo1(void)
{
nrf_memobj_t * p_obj = get_from_foo();
// Claim the object
// Process
//Release the object
}
void foo2(void)
{
nrf_memobj_t * p_obj = get_from_foo();
// Claim the object
// Process
//Release the object
}

Documentation feedback | Developer Zone | Subscribe | Updated