-
Notifications
You must be signed in to change notification settings - Fork 84
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
313 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -102,6 +102,7 @@ set(SRCS | |
mbuf.c | ||
md5.c | ||
mem.c | ||
mem_pool.c | ||
mock/dnssrv.c | ||
mock/nat.c | ||
mock/sipsrv.c | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters