Skip to content

Commit

Permalink
Aligned memory allocation (#4277)
Browse files Browse the repository at this point in the history
* aligned memory allocaion

* Fix alt API implementations (PJ_HAS_POOL_ALT_API)

* pool test: add testing for bug in pj_pool_allocate_find with big alignment, and refactor to use unit test API

* misc fixes on code review

* pool_dbg alignment support + incompatible tests disabled for PJ_HAS_POOL_ALT_API

---------

Co-authored-by: bennylp <bennylp@pjsip.org>
  • Loading branch information
LeonidGoltsblat and bennylp authored Feb 6, 2025
1 parent 2fff775 commit e6196ad
Show file tree
Hide file tree
Showing 8 changed files with 319 additions and 123 deletions.
91 changes: 86 additions & 5 deletions pjlib/include/pj/pool.h
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,10 @@ struct pj_pool_t
/** The callback to be called when the pool is unable to allocate memory. */
pj_pool_callback *callback;

/** The default alignment of memory block allocated from this pool
* (must be power of 2). */
pj_size_t alignment;

};


Expand All @@ -347,8 +351,9 @@ struct pj_pool_t
#endif

/**
* Create a new pool from the pool factory. This wrapper will call create_pool
* member of the pool factory.
* Create a new pool from the pool factory. This wrapper will call
* pj_pool_aligned_create() with alignment parameter set to 0
* which means use the default alignment (PJ_POOL_ALIGNMENT).
*
* @param factory The pool factory.
* @param name The name to be assigned to the pool. The name should
Expand Down Expand Up @@ -379,6 +384,47 @@ PJ_IDECL(pj_pool_t*) pj_pool_create(pj_pool_factory *factory,
pj_size_t increment_size,
pj_pool_callback *callback);


/**
* Create a new pool from the pool factory. This wrapper will call create_pool
* member of the pool factory.
*
* @param factory The pool factory.
* @param name The name to be assigned to the pool. The name should
* not be longer than PJ_MAX_OBJ_NAME (32 chars), or
* otherwise it will be truncated.
* @param initial_size The size of initial memory blocks taken by the pool.
* Note that the pool will take 68+20 bytes for
* administrative area from this block.
* @param increment_size the size of each additional blocks to be allocated
* when the pool is running out of memory. If user
* requests memory which is larger than this size, then
* an error occurs.
* Note that each time a pool allocates additional block,
* it needs PJ_POOL_SIZE more to store some
* administrative info.
* @param alignment the default alignment of memory block allocated
* from this pool (must be power of 2).
* The actual allocation alignment will be at least equal
* to the alignment argument of the function,
* but not less than PJ_POOL_ALIGNMENT.
* Value of 0 means use PJ_POOL_ALIGNMENT.
* @param callback Callback to be called when error occurs in the pool.
* If this value is NULL, then the callback from pool
* factory policy will be used.
* Note that when an error occurs during pool creation,
* the callback itself is not called. Instead, NULL
* will be returned.
*
* @return The memory pool, or NULL.
*/
PJ_IDECL(pj_pool_t*) pj_pool_aligned_create(pj_pool_factory *factory,
const char *name,
pj_size_t initial_size,
pj_size_t increment_size,
pj_size_t alignment,
pj_pool_callback *callback);

/**
* Release the pool back to pool factory.
*
Expand Down Expand Up @@ -448,8 +494,10 @@ PJ_IDECL(pj_size_t) pj_pool_get_used_size( pj_pool_t *pool );

/**
* Allocate storage with the specified size from the pool.
* The allocation will be aligned to the default alignment of the pool.
* If there's no storage available in the pool, then the pool can allocate more
* blocks if the increment size is larger than the requested size.
* This function will call pj_pool_aligned_alloc() with alignment set to 0.
*
* @param pool the pool.
* @param size the requested size.
Expand All @@ -460,6 +508,28 @@ PJ_IDECL(pj_size_t) pj_pool_get_used_size( pj_pool_t *pool );
*/
PJ_IDECL(void*) pj_pool_alloc( pj_pool_t *pool, pj_size_t size);


/**
* Allocate storage with the specified size and alignment from the pool.
* If there's no storage available in the pool, then the pool can allocate more
* blocks if the increment size is larger than the requested size.
*
* @param pool the pool.
* @param alignment the requested alignment of the allocation.
* The actual alignment of the allocation will be at least
* equal to the alignment argument of the function,
* but not less than the default pool alignment specified
* when the pool was created.
* Value of 0 means use the default alignment of this pool.
* @param size the requested size.
*
* @return pointer to the allocated memory.
*
* @see PJ_POOL_ALLOC_T
*/
PJ_IDECL(void *) pj_pool_aligned_alloc(pj_pool_t *pool, pj_size_t alignment,
pj_size_t size);

/**
* Allocate storage from the pool, and initialize it to zero.
* This function behaves like pj_pool_alloc(), except that the storage will
Expand Down Expand Up @@ -523,9 +593,11 @@ PJ_INLINE(void*) pj_pool_zalloc(pj_pool_t *pool, pj_size_t size)
* Internal functions
*/
/** Internal function */
PJ_IDECL(void*) pj_pool_alloc_from_block(pj_pool_block *block, pj_size_t size);
PJ_IDECL(void*) pj_pool_alloc_from_block(pj_pool_block *block, pj_size_t alignment,
pj_size_t size);
/** Internal function */
PJ_DECL(void*) pj_pool_allocate_find(pj_pool_t *pool, pj_size_t size);
PJ_DECL(void*) pj_pool_allocate_find(pj_pool_t *pool, pj_size_t alignment,
pj_size_t size);



Expand Down Expand Up @@ -687,7 +759,11 @@ struct pj_pool_factory
* Note that each time a pool allocates additional block,
* it needs 20 bytes (equal to sizeof(pj_pool_block)) to
* store some administrative info.
* @param callback Cllback to be called when error occurs in the pool.
* @param alignment the default alignment of memory block allocated
* from this pool (must be power of 2).
* A value of 0 should result in the pool being created
* with alignment equal to PJ_POOL_ALIGNMENT.
* @param callback Callback to be called when error occurs in the pool.
* Note that when an error occurs during pool creation,
* the callback itself is not called. Instead, NULL
* will be returned.
Expand All @@ -698,6 +774,7 @@ struct pj_pool_factory
const char *name,
pj_size_t initial_size,
pj_size_t increment_size,
pj_size_t alignment,
pj_pool_callback *callback);

/**
Expand Down Expand Up @@ -748,25 +825,29 @@ struct pj_pool_factory
* @param name Pool name.
* @param initial_size Initial size.
* @param increment_size Increment size.
* @param alignment Pool alignment.
* @param callback Callback.
* @return The pool object, or NULL.
*/
PJ_DECL(pj_pool_t*) pj_pool_create_int( pj_pool_factory *factory,
const char *name,
pj_size_t initial_size,
pj_size_t increment_size,
pj_size_t alignment,
pj_pool_callback *callback);

/**
* This function is intended to be used by pool factory implementors.
* @param pool The pool.
* @param name Pool name.
* @param increment_size Increment size.
* @param alignment Pool alignment.
* @param callback Callback function.
*/
PJ_DECL(void) pj_pool_init_int( pj_pool_t *pool,
const char *name,
pj_size_t increment_size,
pj_size_t alignment,
pj_pool_callback *callback);

/**
Expand Down
13 changes: 10 additions & 3 deletions pjlib/include/pj/pool_alt.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ struct pj_pool_t
char obj_name[32];
pj_size_t used_size;
pj_pool_callback *cb;
pj_size_t alignment;
};


Expand All @@ -68,7 +69,9 @@ PJ_DECL(int) pj_NO_MEMORY_EXCEPTION(void);
* function.
*/
#define pj_pool_create(fc,nm,init,inc,cb) \
pj_pool_create_imp(__FILE__, __LINE__, fc, nm, init, inc, cb)
pj_pool_create_imp(__FILE__, __LINE__, fc, nm, init, inc, 0, cb)
#define pj_pool_aligned_create(fc,nm,init,inc,alg,cb) \
pj_pool_create_imp(__FILE__, __LINE__, fc, nm, init, inc, alg, cb)

#define pj_pool_release(pool) pj_pool_release_imp(pool)
#define pj_pool_safe_release(pool) pj_pool_safe_release_imp(pool)
Expand All @@ -78,7 +81,9 @@ PJ_DECL(int) pj_NO_MEMORY_EXCEPTION(void);
#define pj_pool_get_capacity(pool) pj_pool_get_capacity_imp(pool)
#define pj_pool_get_used_size(pool) pj_pool_get_used_size_imp(pool)
#define pj_pool_alloc(pool,sz) \
pj_pool_alloc_imp(__FILE__, __LINE__, pool, sz)
pj_pool_alloc_imp(__FILE__, __LINE__, pool, 0, sz)
#define pj_pool_aligned_alloc(pool, alignment, sz) \
pj_pool_alloc_imp(__FILE__, __LINE__, pool, alignment, sz)

#define pj_pool_calloc(pool,cnt,elem) \
pj_pool_calloc_imp(__FILE__, __LINE__, pool, cnt, elem)
Expand All @@ -98,6 +103,7 @@ PJ_DECL(pj_pool_t*) pj_pool_create_imp(const char *file, int line,
const char *name,
pj_size_t initial_size,
pj_size_t increment_size,
pj_size_t alignment,
pj_pool_callback *callback);

/* Release pool */
Expand All @@ -123,7 +129,8 @@ PJ_DECL(pj_size_t) pj_pool_get_used_size_imp(pj_pool_t *pool);

/* Allocate memory from the pool */
PJ_DECL(void*) pj_pool_alloc_imp(const char *file, int line,
pj_pool_t *pool, pj_size_t sz);
pj_pool_t *pool, pj_size_t alignment,
pj_size_t sz);

/* Allocate memory from the pool and zero the memory */
PJ_DECL(void*) pj_pool_calloc_imp(const char *file, int line,
Expand Down
72 changes: 58 additions & 14 deletions pjlib/include/pj/pool_i.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@

#include <pj/string.h>

#define PJ_POOL_ALIGN_PTR(PTR,ALIGNMENT) (PTR + (-(pj_ssize_t)(PTR) & (ALIGNMENT-1)))
#define PJ_IS_POWER_OF_TWO(val) (((val)>0) && ((val) & ((val)-1))==0)
#define PJ_IS_ALIGNED(PTR, ALIGNMENT) (!((pj_ssize_t)(PTR) & ((ALIGNMENT)-1)))

PJ_IDEF(pj_size_t) pj_pool_get_capacity( pj_pool_t *pool )
{
Expand All @@ -37,28 +40,59 @@ PJ_IDEF(pj_size_t) pj_pool_get_used_size( pj_pool_t *pool )
return used_size;
}

PJ_IDEF(void*) pj_pool_alloc_from_block( pj_pool_block *block, pj_size_t size )
PJ_IDEF(void*) pj_pool_alloc_from_block( pj_pool_block *block, pj_size_t alignment,
pj_size_t size )
{
/* The operation below is valid for size==0.
* When size==0, the function will return the pointer to the pool
* memory address, but no memory will be allocated.
*/
if (size & (PJ_POOL_ALIGNMENT-1)) {
size = (size + PJ_POOL_ALIGNMENT) & ~(PJ_POOL_ALIGNMENT-1);
}
if ((pj_size_t)(block->end - block->cur) >= size) {
void *ptr = block->cur;
block->cur += size;
unsigned char *ptr;

pj_assert(PJ_IS_POWER_OF_TWO(alignment) && PJ_IS_ALIGNED(size, alignment));
// Size should be already aligned.
// this code was moved up to pj_pool_aligned_alloc.
///* The operation below is valid for size==0.
// * When size==0, the function will return the pointer to the pool
// * memory address, but no memory will be allocated.
// */
//if (size & (alignment -1)) {
// size = (size + alignment) & ~(alignment -1);
//}
ptr = PJ_POOL_ALIGN_PTR(block->cur, alignment);
if (ptr + size <= block->end &&
/* here we check pointer overflow */
block->cur <= ptr && ptr <= ptr + size) {
block->cur = ptr + size;
return ptr;
}
return NULL;
}

PJ_IDEF(void*) pj_pool_alloc( pj_pool_t *pool, pj_size_t size)
{
void *ptr = pj_pool_alloc_from_block(pool->block_list.next, size);
return pj_pool_aligned_alloc(pool, 0, size);
}

PJ_IDECL(void *) pj_pool_aligned_alloc(pj_pool_t *pool, pj_size_t alignment,
pj_size_t size)
{
void *ptr;

PJ_ASSERT_RETURN(!alignment || PJ_IS_POWER_OF_TWO(alignment), NULL);

if (alignment < pool->alignment)
alignment = pool->alignment;

/* The operation below is valid for size==0.
* When size==0, the function will return the pointer to the pool
* memory address, but no memory will be allocated.
*/
if (size & (alignment -1)) {
size = (size + alignment) & ~(alignment -1);
}
pj_assert(PJ_IS_ALIGNED(size, alignment));

ptr = pj_pool_alloc_from_block(pool->block_list.next,
alignment, size);
if (!ptr)
ptr = pj_pool_allocate_find(pool, size);
ptr = pj_pool_allocate_find(pool, alignment, size);
return ptr;
}

Expand All @@ -82,7 +116,17 @@ PJ_IDEF(pj_pool_t*) pj_pool_create( pj_pool_factory *f,
pj_size_t increment_size,
pj_pool_callback *callback)
{
return (*f->create_pool)(f, name, initial_size, increment_size, callback);
return pj_pool_aligned_create(f, name, initial_size, increment_size, 0, callback);
}

PJ_IDECL(pj_pool_t *) pj_pool_aligned_create(pj_pool_factory *f,
const char *name,
pj_size_t initial_size,
pj_size_t increment_size,
pj_size_t alignment,
pj_pool_callback *callback)
{
return (*f->create_pool)(f, name, initial_size, increment_size, alignment, callback);
}

PJ_IDEF(void) pj_pool_release( pj_pool_t *pool )
Expand Down
Loading

0 comments on commit e6196ad

Please sign in to comment.