Skip to content

Commit

Permalink
mem: add mem_pool
Browse files Browse the repository at this point in the history
  • Loading branch information
sreimers committed Jan 8, 2025
1 parent 816dc5a commit 3c6bf26
Show file tree
Hide file tree
Showing 7 changed files with 313 additions and 0 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,7 @@ set(SRCS
src/md5/wrap.c

src/mem/mem.c
src/mem/mem_pool.c
src/mem/secure.c

src/mod/mod.c
Expand Down
12 changes: 12 additions & 0 deletions include/re_mem.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,15 @@ int mem_get_stat(struct memstat *mstat);
/* Secure memory functions */
int mem_seccmp(const uint8_t *s1, const uint8_t *s2, size_t n);
void mem_secclean(void *data, size_t size);


/* Mem Pool */
struct mem_pool;
struct mem_pool_entry;
int mem_pool_alloc(struct mem_pool **poolp, size_t nmemb, size_t membsize,
mem_destroy_h *dh);
int mem_pool_extend(struct mem_pool *pool, size_t num);
struct mem_pool_entry *mem_pool_borrow(struct mem_pool *pool);
void *mem_pool_release(struct mem_pool *pool,
const struct mem_pool_entry *entry);
void *mem_pool_member(const struct mem_pool_entry *entry);
235 changes: 235 additions & 0 deletions src/mem/mem_pool.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
/**
* @file mem_pool.c Pre-Allocated Memory pool management
*
* Copyright (C) 2025 Sebastian Reimers
*/

#include <string.h>

#include <re_types.h>
#include <re_mem.h>
#include <re_thread.h>
#include <re_atomic.h>


#define DEBUG_MODULE "mem_pool"
#define DEBUG_LEVEL 5
#include <re_dbg.h>


struct mem_pool {
RE_ATOMIC size_t next_free;
size_t nmemb;
size_t membsize;
struct mem_pool_entry *objs;
mem_destroy_h *membdh;
};

struct mem_pool_entry {
RE_ATOMIC bool used;
void *member;
};


static void mem_pool_destroy(void *data)
{
struct mem_pool *p = data;

for (size_t i = 0; i < p->nmemb; i++) {
mem_deref(p->objs[i].member);
}

mem_deref(p->objs);
}


/**
* @brief Allocate a memory pool.
*
* This function initializes a memory pool with a specified number of elements,
* each of a given size. Optionally, a destructor callback can be provided
* to handle cleanup when a member is released or pool is destroyed.
*
* @param poolp Pointer to the memory pool pointer to be initialized.
* @param nmemb Number of elements to allocate in the pool.
* @param membsize Size of each element in the pool.
* @param dh Optional destructor callback for pool cleanup (can be
* NULL).
*
* @return 0 for success, otherwise error code
*/
int mem_pool_alloc(struct mem_pool **poolp, size_t nmemb, size_t membsize,
mem_destroy_h *dh)
{
int err;

if (!poolp || !nmemb || !membsize)
return EINVAL;

struct mem_pool *p = mem_zalloc(sizeof(struct mem_pool), NULL);
if (!p)
return ENOMEM;

p->nmemb = nmemb;
p->membsize = membsize;
p->membdh = dh;
p->objs = mem_zalloc(nmemb * sizeof(struct mem_pool_entry), NULL);
if (!p->objs) {
err = ENOMEM;
goto error;
}

mem_destructor(p, mem_pool_destroy);

for (size_t i = 0; i < nmemb; i++) {
p->objs[i].member = mem_zalloc(membsize, dh);
if (!p->objs[i].member) {
err = ENOMEM;
goto error;
}
}

*poolp = p;

return 0;

error:
mem_deref(p);
return err;
}


/**
* @brief Extend an existing memory pool.
*
* Adds additional elements to an existing memory pool.
*
* @note This function is not thread-safe. Concurrent access to the memory
* pool while extending it may result in undefined behavior. Ensure that
* proper synchronization mechanisms are in place if this function is used
* in a multithreaded context.
*
* @param pool Pointer to the memory pool to extend.
* @param num Number of additional elements to add to the pool.
*
* @return 0 for success, otherwise error code
*/
int mem_pool_extend(struct mem_pool *pool, size_t num)
{
if (!pool || !num)
return EINVAL;

size_t nmemb = pool->nmemb + num;

struct mem_pool_entry *objs;
objs = mem_zalloc(nmemb * sizeof(struct mem_pool_entry), NULL);
if (!objs)
return ENOMEM;

/* Copy old members */
size_t i = 0;
for (; i < pool->nmemb; i++) {
objs[i].member = pool->objs[i].member;
re_atomic_rlx_set(&objs[i].used,
re_atomic_rlx(&pool->objs[i].used));
}

/* Allocate new members */
for (; i < nmemb; i++) {
objs[i].member = mem_zalloc(pool->membsize, NULL);
if (!objs[i].member) {
mem_deref(objs);
return ENOMEM;
}
}

mem_deref(pool->objs);
pool->objs = objs;
pool->nmemb = nmemb;

return 0;
}


/**
* @brief Borrow an entry from the memory pool.
*
* Retrieves an unused entry from the memory pool for temporary use.
*
* @param pool Pointer to the memory pool.
*
* @return Pointer to a memory pool entry, or NULL if no entries are available.
*/
struct mem_pool_entry *mem_pool_borrow(struct mem_pool *pool)
{
if (!pool)
return NULL;

for (size_t i = re_atomic_acq(&pool->next_free); i < pool->nmemb;
i++) {
if (!re_atomic_acq(&pool->objs[i].used)) {
re_atomic_rls_set(&pool->objs[i].used, true);
re_atomic_rls_set(&pool->next_free, i + 1);
return &(pool->objs[i]);
}
}

for (size_t i = 0; i < pool->nmemb; i++) {
if (!re_atomic_acq(&pool->objs[i].used)) {
re_atomic_rls_set(&pool->objs[i].used, true);
re_atomic_rls_set(&pool->next_free, i + 1);
return &(pool->objs[i]);
}
}

return NULL;
}


/**
* @brief Release a borrowed entry back to the memory pool.
*
* Returns a previously borrowed memory pool entry back to the pool.
* When the entry is released, the member destructor callback (if provided)
* is called to perform any necessary cleanup. Additionally, the memory
* associated with the entry is re-initialized to zero to ensure a clean state
* for future use.
*
* @param pool Pointer to the memory pool.
* @param entry Pointer to the memory pool entry to release.
*
* @return Always NULL
*/
void *mem_pool_release(struct mem_pool *pool,
const struct mem_pool_entry *entry)
{
if (!pool || !entry)
return NULL;

size_t i = ((uintptr_t)entry - (uintptr_t)pool->objs) /
sizeof(struct mem_pool_entry);
re_assert(i < pool->nmemb);
re_assert(re_atomic_rlx(&pool->objs[i].used));
re_atomic_rls_set(&pool->objs[i].used, false);
re_atomic_rls_set(&pool->next_free, i);

if (pool->membdh)
pool->membdh(pool->objs[i].member);

memset(pool->objs[i].member, 0, pool->membsize);

return NULL;
}


/**
* Return Pool member
*
* @param entry Pointer to the memory pool entry.
*
* @return Pointer to the data associated with the memory pool entry or NULL
*/
void *mem_pool_member(const struct mem_pool_entry *entry)
{
return entry ? entry->member : NULL;
}
1 change: 1 addition & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ set(SRCS
mbuf.c
md5.c
mem.c
mem_pool.c
mock/dnssrv.c
mock/nat.c
mock/sipsrv.c
Expand Down
62 changes: 62 additions & 0 deletions test/mem_pool.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/**
* @file mem_pool.c Memory Pool Testcode
*
* Copyright (C) 2025 Sebastian Reimers
*/
#include <re.h>
#include "test.h"


#define DEBUG_MODULE "test_mem_pool"
#define DEBUG_LEVEL 5
#include <re_dbg.h>

struct object {
int a;
};

enum {
NUM_OBJECTS = 10,
};


int test_mem_pool(void)
{
int err;

struct mem_pool *pool = NULL;
err = mem_pool_alloc(&pool, NUM_OBJECTS, sizeof(struct object), NULL);
TEST_ERR(err);

struct mem_pool_entry *e;
struct object *o;

for (int i = 0; i < NUM_OBJECTS; i++) {
e = mem_pool_borrow(pool);
TEST_ASSERT(e);

o = mem_pool_member(e);
TEST_NOT_EQUALS(o->a, i + 1);

o->a = i + 1;
}

TEST_ASSERT(!mem_pool_borrow(pool));

e = mem_pool_release(pool, e);
e = mem_pool_borrow(pool);
TEST_ASSERT(e);

TEST_ASSERT(!mem_pool_borrow(pool));

err = mem_pool_extend(pool, 1);
TEST_ERR(err);
e = mem_pool_borrow(pool);
TEST_ASSERT(e);

TEST_ASSERT(!mem_pool_borrow(pool));

out:
mem_deref(pool);
return err;
}
1 change: 1 addition & 0 deletions test/test.c
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ static const struct test tests[] = {
TEST(test_mbuf),
TEST(test_md5),
TEST(test_mem),
TEST(test_mem_pool),
TEST(test_mem_reallocarray),
TEST(test_mem_secure),
TEST(test_net_if),
Expand Down
1 change: 1 addition & 0 deletions test/test.h
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,7 @@ int test_list_sort(void);
int test_mbuf(void);
int test_md5(void);
int test_mem(void);
int test_mem_pool(void);
int test_mem_reallocarray(void);
int test_mem_secure(void);
int test_mqueue(void);
Expand Down

0 comments on commit 3c6bf26

Please sign in to comment.