mirror of
https://github.com/signalwire/freeswitch.git
synced 2025-04-14 16:15:04 +00:00
1274 lines
27 KiB
C
1274 lines
27 KiB
C
/*
|
|
* Memory pool routines.
|
|
*
|
|
* Copyright 1996 by Gray Watson.
|
|
*
|
|
* This file is part of the ks_pool package.
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software for
|
|
* any purpose and without fee is hereby granted, provided that the
|
|
* above copyright notice and this permission notice appear in all
|
|
* copies, and that the name of Gray Watson not be used in advertising
|
|
* or publicity pertaining to distribution of the document or software
|
|
* without specific, written prior permission.
|
|
*
|
|
* Gray Watson makes no representations about the suitability of the
|
|
* software described herein for any purpose. It is provided "as is"
|
|
* without express or implied warranty.
|
|
*
|
|
* The author may be reached via http://256.com/gray/
|
|
*
|
|
* $Id: ks_mpool.c,v 1.5 2006/05/31 20:28:31 gray Exp $
|
|
*/
|
|
|
|
/*
|
|
* Memory-pool allocation routines. I got sick of the GNU mmalloc
|
|
* library which was close to what we needed but did not exactly do
|
|
* what I wanted.
|
|
*
|
|
*/
|
|
|
|
#include "ks.h"
|
|
|
|
//#define DEBUG 1
|
|
|
|
#define KS_POOL_MAGIC 0xDEADBEEF /* magic for struct */
|
|
|
|
#define KS_POOL_PREFIX_MAGIC 0xDEADBEEF
|
|
|
|
#define KS_POOL_FENCE_MAGIC0 (ks_byte_t)(0xFAU) /* 1st magic mem byte */
|
|
#define KS_POOL_FENCE_MAGIC1 (ks_byte_t)(0xD3U) /* 2nd magic mem byte */
|
|
|
|
#define KS_POOL_FENCE_SIZE 2 /* fence space */
|
|
|
|
typedef struct ks_pool_prefix_s ks_pool_prefix_t;
|
|
|
|
struct ks_pool_prefix_s {
|
|
ks_size_t magic1;
|
|
ks_size_t size;
|
|
ks_size_t magic2;
|
|
ks_size_t refs;
|
|
ks_pool_prefix_t *prev;
|
|
ks_pool_prefix_t *next;
|
|
ks_size_t magic3;
|
|
ks_pool_cleanup_callback_t cleanup_callback;
|
|
void *cleanup_arg;
|
|
ks_size_t magic4;
|
|
ks_size_t reserved[2];
|
|
};
|
|
|
|
#define KS_POOL_PREFIX_SIZE sizeof(ks_pool_prefix_t)
|
|
|
|
#define SET_POINTER(pnt, val) \
|
|
do { \
|
|
if ((pnt) != NULL) { \
|
|
(*(pnt)) = (val); \
|
|
} \
|
|
} while(0)
|
|
|
|
struct ks_pool_s {
|
|
ks_size_t magic1; /* magic number for struct */
|
|
ks_size_t flags; /* flags for the struct */
|
|
ks_size_t alloc_c; /* number of allocations */
|
|
ks_size_t user_alloc; /* user bytes allocated */
|
|
ks_size_t max_alloc; /* maximum user bytes allocated */
|
|
ks_pool_log_func_t log_func; /* log callback function */
|
|
ks_pool_prefix_t *first; /* first memory allocation we are using */
|
|
ks_pool_prefix_t *last; /* last memory allocation we are using */
|
|
ks_size_t magic2; /* upper magic for overwrite sanity */
|
|
ks_mutex_t *mutex;
|
|
ks_bool_t cleaning_up;
|
|
};
|
|
|
|
static ks_status_t check_pool(const ks_pool_t *pool);
|
|
static ks_status_t check_fence(const void *addr);
|
|
static void write_fence(void *addr);
|
|
static ks_status_t check_prefix(const ks_pool_prefix_t *prefix);
|
|
|
|
static void perform_pool_cleanup_on_free(ks_pool_t *pool, ks_pool_prefix_t *prefix)
|
|
{
|
|
void *addr;
|
|
|
|
ks_assert(pool);
|
|
ks_assert(prefix);
|
|
|
|
if (pool->cleaning_up) return;
|
|
|
|
addr = (void *)((uintptr_t)prefix + KS_POOL_PREFIX_SIZE);
|
|
|
|
if (prefix->cleanup_callback) {
|
|
prefix->cleanup_callback(pool, addr, prefix->cleanup_arg, KS_MPCL_ANNOUNCE, KS_MPCL_FREE);
|
|
prefix->cleanup_callback(pool, addr, prefix->cleanup_arg, KS_MPCL_TEARDOWN, KS_MPCL_FREE);
|
|
prefix->cleanup_callback(pool, addr, prefix->cleanup_arg, KS_MPCL_DESTROY, KS_MPCL_FREE);
|
|
}
|
|
}
|
|
|
|
static void perform_pool_cleanup(ks_pool_t *pool)
|
|
{
|
|
ks_pool_prefix_t *prefix;
|
|
|
|
if (pool->cleaning_up) {
|
|
return;
|
|
}
|
|
pool->cleaning_up = KS_TRUE;
|
|
|
|
for (prefix = pool->first; prefix; prefix = prefix->next) {
|
|
if (!prefix->cleanup_callback) continue;
|
|
prefix->cleanup_callback(pool, (void *)((uintptr_t)prefix + KS_POOL_PREFIX_SIZE), prefix->cleanup_arg, KS_MPCL_ANNOUNCE, KS_MPCL_GLOBAL_FREE);
|
|
}
|
|
|
|
for (prefix = pool->first; prefix; prefix = prefix->next) {
|
|
if (!prefix->cleanup_callback) continue;
|
|
prefix->cleanup_callback(pool, (void *)((uintptr_t)prefix + KS_POOL_PREFIX_SIZE), prefix->cleanup_arg, KS_MPCL_TEARDOWN, KS_MPCL_GLOBAL_FREE);
|
|
}
|
|
|
|
for (prefix = pool->first; prefix; prefix = prefix->next) {
|
|
if (!prefix->cleanup_callback) continue;
|
|
prefix->cleanup_callback(pool, (void *)((uintptr_t)prefix + KS_POOL_PREFIX_SIZE), prefix->cleanup_arg, KS_MPCL_DESTROY, KS_MPCL_GLOBAL_FREE);
|
|
}
|
|
}
|
|
|
|
KS_DECLARE(ks_status_t) ks_pool_set_cleanup(ks_pool_t *pool, void *ptr, void *arg, ks_pool_cleanup_callback_t callback)
|
|
{
|
|
ks_status_t ret = KS_STATUS_SUCCESS;
|
|
ks_pool_prefix_t *prefix = NULL;
|
|
|
|
ks_assert(pool);
|
|
ks_assert(ptr);
|
|
ks_assert(callback);
|
|
|
|
prefix = (ks_pool_prefix_t *)((uintptr_t)ptr - KS_POOL_PREFIX_SIZE);
|
|
|
|
ret = check_prefix(prefix);
|
|
|
|
if (ret == KS_STATUS_SUCCESS) {
|
|
prefix->cleanup_arg = arg;
|
|
prefix->cleanup_callback = callback;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
|
|
/****************************** local utilities ******************************/
|
|
|
|
/*
|
|
* static ks_status_t check_pool
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Check the validity of pool checksums.
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* Success - KS_STATUS_SUCCESS
|
|
*
|
|
* Failure - Ks_Pool error code
|
|
*
|
|
* ARGUMENTS:
|
|
*
|
|
* pool -> A pointer to a pool.
|
|
*/
|
|
static ks_status_t check_pool(const ks_pool_t *pool)
|
|
{
|
|
ks_assert(pool);
|
|
|
|
if (pool->magic1 != KS_POOL_MAGIC) return KS_STATUS_PNT;
|
|
if (pool->magic2 != KS_POOL_MAGIC) return KS_STATUS_POOL_OVER;
|
|
|
|
return KS_STATUS_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* static ks_status_t check_fence
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Check the validity of the fence checksums.
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* Success - KS_STATUS_SUCCESS
|
|
*
|
|
* Failure - Ks_Pool error code
|
|
*
|
|
* ARGUMENTS:
|
|
*
|
|
* addr -> A pointer directly to the fence.
|
|
*/
|
|
static ks_status_t check_fence(const void *addr)
|
|
{
|
|
const ks_byte_t *mem_p;
|
|
|
|
mem_p = (ks_byte_t *)addr;
|
|
|
|
if (*mem_p == KS_POOL_FENCE_MAGIC0 && *(mem_p + 1) == KS_POOL_FENCE_MAGIC1) return KS_STATUS_SUCCESS;
|
|
return KS_STATUS_PNT_OVER;
|
|
}
|
|
|
|
/*
|
|
* static void write_fence
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Write the magic ID to the address.
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* None.
|
|
*
|
|
* ARGUMENTS:
|
|
*
|
|
* addr -> Address where to write the magic.
|
|
*/
|
|
static void write_fence(void *addr)
|
|
{
|
|
*((ks_byte_t *)addr) = KS_POOL_FENCE_MAGIC0;
|
|
*((ks_byte_t *)addr + 1) = KS_POOL_FENCE_MAGIC1;
|
|
}
|
|
|
|
/*
|
|
* static ks_status_t check_prefix
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Check the validity of prefix checksums.
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* Success - KS_STATUS_SUCCESS
|
|
*
|
|
* Failure - Ks_Pool error code
|
|
*
|
|
* ARGUMENTS:
|
|
*
|
|
* prefix -> A pointer to a prefix.
|
|
*/
|
|
static ks_status_t check_prefix(const ks_pool_prefix_t *prefix)
|
|
{
|
|
if (!(prefix->magic1 == KS_POOL_PREFIX_MAGIC && prefix->magic2 == KS_POOL_PREFIX_MAGIC && prefix->magic3 == KS_POOL_PREFIX_MAGIC && prefix->magic4 == KS_POOL_PREFIX_MAGIC)) return KS_STATUS_INVALID_POINTER;
|
|
return KS_STATUS_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* static void *alloc_mem
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Allocate space for bytes inside of an already open memory pool.
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* Success - Pointer to the address to use.
|
|
*
|
|
* Failure - NULL
|
|
*
|
|
* ARGUMENTS:
|
|
*
|
|
* pool -> Pointer to the memory pool.
|
|
*
|
|
* byte_size -> Number of bytes to allocate in the pool. Must be >0.
|
|
*
|
|
* error_p <- Pointer to ks_status_t which, if not NULL, will be set with
|
|
* a ks_pool error code.
|
|
*/
|
|
static void *alloc_mem(ks_pool_t *pool, const ks_size_t size, ks_status_t *error_p)
|
|
{
|
|
ks_size_t required;
|
|
void *start = NULL;
|
|
void *addr = NULL;
|
|
void *fence = NULL;
|
|
ks_pool_prefix_t *prefix = NULL;
|
|
|
|
ks_assert(pool);
|
|
ks_assert(size);
|
|
|
|
required = KS_POOL_PREFIX_SIZE + size + KS_POOL_FENCE_SIZE;
|
|
start = malloc(required);
|
|
ks_assert(start);
|
|
memset(start, 0, required);
|
|
|
|
prefix = (ks_pool_prefix_t *)start;
|
|
addr = (void *)((ks_byte_t *)start + KS_POOL_PREFIX_SIZE);
|
|
fence = (void *)((ks_byte_t *)addr + size);
|
|
|
|
prefix->magic1 = KS_POOL_PREFIX_MAGIC;
|
|
prefix->size = size;
|
|
prefix->magic2 = KS_POOL_PREFIX_MAGIC;
|
|
prefix->refs = 1;
|
|
prefix->next = pool->first;
|
|
if (pool->first) pool->first->prev = prefix;
|
|
pool->first = prefix;
|
|
if (!pool->last) pool->last = prefix;
|
|
prefix->magic3 = KS_POOL_PREFIX_MAGIC;
|
|
prefix->magic4 = KS_POOL_PREFIX_MAGIC;
|
|
|
|
write_fence(fence);
|
|
|
|
if (pool->log_func != NULL) {
|
|
pool->log_func(pool, KS_POOL_FUNC_INCREF, prefix->size, prefix->refs, NULL, addr, 0);
|
|
}
|
|
|
|
pool->alloc_c++;
|
|
pool->user_alloc += prefix->size;
|
|
if (pool->user_alloc > pool->max_alloc) {
|
|
pool->max_alloc = pool->user_alloc;
|
|
}
|
|
|
|
SET_POINTER(error_p, KS_STATUS_SUCCESS);
|
|
return addr;
|
|
}
|
|
|
|
/*
|
|
* static int free_mem
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Free an address from a memory pool.
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* Success - KS_STATUS_SUCCESS
|
|
*
|
|
* Failure - Ks_Pool error code
|
|
*
|
|
* ARGUMENTS:
|
|
*
|
|
* pool -> Pointer to the memory pool.
|
|
*
|
|
* addr -> Address to free.
|
|
*
|
|
*/
|
|
static ks_status_t free_mem(ks_pool_t *pool, void *addr)
|
|
{
|
|
ks_status_t ret = KS_STATUS_SUCCESS;
|
|
void *start = NULL;
|
|
void *fence = NULL;
|
|
ks_pool_prefix_t *prefix = NULL;
|
|
|
|
ks_assert(pool);
|
|
ks_assert(addr);
|
|
|
|
start = (void *)((uintptr_t)addr - KS_POOL_PREFIX_SIZE);
|
|
prefix = (ks_pool_prefix_t *)start;
|
|
|
|
if ((ret = check_prefix(prefix)) != KS_STATUS_SUCCESS) return ret;
|
|
|
|
if (prefix->refs > 0) {
|
|
prefix->refs--;
|
|
|
|
if (pool->log_func != NULL) {
|
|
pool->log_func(pool, KS_POOL_FUNC_DECREF, prefix->size, prefix->refs, addr, NULL, 0);
|
|
}
|
|
}
|
|
|
|
if (prefix->refs > 0) {
|
|
return KS_STATUS_REFS_EXIST;
|
|
}
|
|
|
|
fence = (void *)((uintptr_t)addr + prefix->size);
|
|
ret = check_fence(fence);
|
|
|
|
perform_pool_cleanup_on_free(pool, prefix);
|
|
|
|
if (!prefix->prev && !prefix->next) pool->first = pool->last = NULL;
|
|
else if (!prefix->prev) {
|
|
pool->first = prefix->next;
|
|
pool->first->prev = NULL;
|
|
}
|
|
else if (!prefix->next) {
|
|
pool->last = prefix->prev;
|
|
pool->last->next = NULL;
|
|
} else {
|
|
prefix->prev->next = prefix->next;
|
|
prefix->next->prev = prefix->prev;
|
|
}
|
|
|
|
pool->alloc_c--;
|
|
pool->user_alloc -= prefix->size;
|
|
|
|
free(start);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/***************************** exported routines *****************************/
|
|
|
|
/*
|
|
* ks_pool_t *ks_pool_open
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Open/allocate a new memory pool.
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* Success - Pool pointer which must be passed to ks_pool_close to
|
|
* deallocate.
|
|
*
|
|
* Failure - NULL
|
|
*
|
|
* ARGUMENTS:
|
|
*
|
|
* flags -> Flags to set attributes of the memory pool. See the top
|
|
* of ks_pool.h.
|
|
*
|
|
* error_p <- Pointer to ks_status_t which, if not NULL, will be set with
|
|
* a ks_pool error code.
|
|
*/
|
|
static ks_pool_t *ks_pool_raw_open(const ks_size_t flags, ks_status_t *error_p)
|
|
{
|
|
ks_pool_t *pool;
|
|
|
|
pool = malloc(sizeof(ks_pool_t));
|
|
ks_assert(pool);
|
|
memset(pool, 0, sizeof(ks_pool_t));
|
|
|
|
pool->magic1 = KS_POOL_MAGIC;
|
|
pool->flags = flags;
|
|
pool->magic2 = KS_POOL_MAGIC;
|
|
|
|
SET_POINTER(error_p, KS_STATUS_SUCCESS);
|
|
return pool;
|
|
}
|
|
|
|
/*
|
|
* ks_pool_t *ks_pool_open
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Open/allocate a new memory pool.
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* Success - KS_SUCCESS
|
|
*
|
|
* Failure - KS_FAIL
|
|
*
|
|
* ARGUMENTS:
|
|
*
|
|
* poolP <- pointer to new pool that will be set on success
|
|
*
|
|
*/
|
|
|
|
KS_DECLARE(ks_status_t) ks_pool_open(ks_pool_t **poolP)
|
|
{
|
|
ks_status_t ret = KS_STATUS_SUCCESS;
|
|
ks_pool_t *pool = NULL;
|
|
|
|
ks_assert(poolP);
|
|
|
|
pool = ks_pool_raw_open(KS_POOL_FLAG_DEFAULT, &ret);
|
|
|
|
*poolP = pool;
|
|
|
|
if (pool) ks_mutex_create(&pool->mutex, KS_MUTEX_FLAG_DEFAULT, NULL);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* int ks_pool_raw_close
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Close/free a memory allocation pool previously opened with
|
|
* ks_pool_open.
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* Success - KS_STATUS_SUCCESS
|
|
*
|
|
* Failure - Ks_Pool error code
|
|
*
|
|
* ARGUMENTS:
|
|
*
|
|
* pool -> Pointer to our memory pool.
|
|
*/
|
|
static ks_status_t ks_pool_raw_close(ks_pool_t *pool)
|
|
{
|
|
ks_status_t ret = KS_STATUS_SUCCESS;
|
|
|
|
if ((ret = ks_pool_clear(pool)) != KS_STATUS_SUCCESS) goto done;
|
|
|
|
if (pool->log_func != NULL) {
|
|
pool->log_func(pool, KS_POOL_FUNC_CLOSE, 0, 0, NULL, NULL, 0);
|
|
}
|
|
|
|
ks_mutex_destroy(&pool->mutex);
|
|
|
|
free(pool);
|
|
|
|
done:
|
|
ks_assert(ret == KS_STATUS_SUCCESS);
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
* ks_status_t ks_pool_close
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Close/free a memory allocation pool previously opened with
|
|
* ks_pool_open.
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* Success - KS_STATUS_SUCCESS
|
|
*
|
|
* Failure - ks_status_t error code
|
|
*
|
|
* ARGUMENTS:
|
|
*
|
|
* poolP <-> Pointer to pointer of our memory pool.
|
|
*/
|
|
|
|
KS_DECLARE(ks_status_t) ks_pool_close(ks_pool_t **poolP)
|
|
{
|
|
ks_status_t ret = KS_STATUS_SUCCESS;
|
|
|
|
ks_assert(poolP);
|
|
ks_assert(*poolP);
|
|
|
|
if ((ret = ks_pool_raw_close(*poolP)) == KS_STATUS_SUCCESS) *poolP = NULL;
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* int ks_pool_clear
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Wipe an opened memory pool clean so we can start again.
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* Success - KS_STATUS_SUCCESS
|
|
*
|
|
* Failure - Ks_Pool error code
|
|
*
|
|
* ARGUMENTS:
|
|
*
|
|
* pool -> Pointer to our memory pool.
|
|
*/
|
|
KS_DECLARE(ks_status_t) ks_pool_clear(ks_pool_t *pool)
|
|
{
|
|
ks_status_t ret = KS_STATUS_SUCCESS;
|
|
ks_pool_prefix_t *prefix, *nprefix;
|
|
|
|
ks_assert(pool);
|
|
|
|
if ((ret = check_pool(pool)) != KS_STATUS_SUCCESS) goto done;
|
|
|
|
if (pool->log_func != NULL) {
|
|
pool->log_func(pool, KS_POOL_FUNC_CLEAR, 0, 0, NULL, NULL, 0);
|
|
}
|
|
|
|
ks_mutex_lock(pool->mutex);
|
|
|
|
perform_pool_cleanup(pool);
|
|
|
|
for (prefix = pool->first; prefix; prefix = nprefix) {
|
|
nprefix = prefix->next;
|
|
// @todo check_prefix()? still want to clear out properly if some has been cleared though, not leak memory if there has been corruption
|
|
free(prefix);
|
|
}
|
|
|
|
ks_mutex_unlock(pool->mutex);
|
|
|
|
done:
|
|
ks_assert(ret == KS_STATUS_SUCCESS);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* void *ks_pool_alloc_ex
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Allocate space for bytes inside of an already open memory pool.
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* Success - Pointer to the address to use.
|
|
*
|
|
* Failure - NULL
|
|
*
|
|
* ARGUMENTS:
|
|
*
|
|
* pool -> Pointer to the memory pool.
|
|
*
|
|
* size -> Number of bytes to allocate in the pool. Must be >0.
|
|
*
|
|
* error_p <- Pointer to integer which, if not NULL, will be set with
|
|
* a ks_pool error code.
|
|
*/
|
|
KS_DECLARE(void *) ks_pool_alloc_ex(ks_pool_t *pool, const ks_size_t size, ks_status_t *error_p)
|
|
{
|
|
ks_status_t ret = KS_STATUS_SUCCESS;
|
|
void *addr = NULL;
|
|
|
|
ks_assert(pool);
|
|
ks_assert(size);
|
|
|
|
if ((ret = check_pool(pool)) != KS_STATUS_SUCCESS) goto done;
|
|
|
|
ks_mutex_lock(pool->mutex);
|
|
addr = alloc_mem(pool, size, &ret);
|
|
ks_mutex_unlock(pool->mutex);
|
|
|
|
if (pool->log_func != NULL) {
|
|
pool->log_func(pool, KS_POOL_FUNC_ALLOC, size, 0, addr, NULL, 0);
|
|
}
|
|
|
|
ks_assert(addr);
|
|
|
|
done:
|
|
ks_assert(ret == KS_STATUS_SUCCESS);
|
|
return addr;
|
|
}
|
|
|
|
/*
|
|
* void *ks_pool_alloc
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Allocate space for bytes inside of an already open memory pool.
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* Success - Pointer to the address to use.
|
|
*
|
|
* Failure - NULL
|
|
*
|
|
* ARGUMENTS:
|
|
*
|
|
* pool -> Pointer to the memory pool.
|
|
*
|
|
*
|
|
* size -> Number of bytes to allocate in the pool. Must be >0.
|
|
*
|
|
*/
|
|
KS_DECLARE(void *) ks_pool_alloc(ks_pool_t *pool, const ks_size_t size)
|
|
{
|
|
return ks_pool_alloc_ex(pool, size, NULL);
|
|
}
|
|
|
|
|
|
/*
|
|
* void *ks_pool_calloc_ex
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Allocate space for elements of bytes in the memory pool and zero
|
|
* the space afterwards.
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* Success - Pointer to the address to use.
|
|
*
|
|
* Failure - NULL
|
|
*
|
|
* ARGUMENTS:
|
|
*
|
|
* pool -> Pointer to the memory pool. If NULL then it will do a
|
|
* normal calloc.
|
|
*
|
|
* ele_n -> Number of elements to allocate.
|
|
*
|
|
* ele_size -> Number of bytes per element being allocated.
|
|
*
|
|
* error_p <- Pointer to integer which, if not NULL, will be set with
|
|
* a ks_pool error code.
|
|
*/
|
|
KS_DECLARE(void *) ks_pool_calloc_ex(ks_pool_t *pool, const ks_size_t ele_n, const ks_size_t ele_size, ks_status_t *error_p)
|
|
{
|
|
ks_status_t ret = KS_STATUS_SUCCESS;
|
|
void *addr = NULL;
|
|
ks_size_t size;
|
|
|
|
ks_assert(pool);
|
|
ks_assert(ele_n);
|
|
ks_assert(ele_size);
|
|
|
|
if ((ret = check_pool(pool)) != KS_STATUS_SUCCESS) goto done;
|
|
|
|
size = ele_n * ele_size;
|
|
|
|
ks_mutex_lock(pool->mutex);
|
|
addr = alloc_mem(pool, size, &ret);
|
|
ks_mutex_unlock(pool->mutex);
|
|
|
|
if (pool->log_func != NULL) {
|
|
pool->log_func(pool, KS_POOL_FUNC_CALLOC, ele_size, ele_n, addr, NULL, 0);
|
|
}
|
|
|
|
ks_assert(addr);
|
|
|
|
done:
|
|
ks_assert(ret == KS_STATUS_SUCCESS);
|
|
|
|
return addr;
|
|
}
|
|
|
|
/*
|
|
* void *ks_pool_calloc
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Allocate space for elements of bytes in the memory pool and zero
|
|
* the space afterwards.
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* Success - Pointer to the address to use.
|
|
*
|
|
* Failure - NULL
|
|
*
|
|
* ARGUMENTS:
|
|
*
|
|
* pool -> Pointer to the memory pool. If NULL then it will do a
|
|
* normal calloc.
|
|
*
|
|
* ele_n -> Number of elements to allocate.
|
|
*
|
|
* ele_size -> Number of bytes per element being allocated.
|
|
*
|
|
*/
|
|
KS_DECLARE(void *) ks_pool_calloc(ks_pool_t *pool, const ks_size_t ele_n, const ks_size_t ele_size)
|
|
{
|
|
return ks_pool_calloc_ex(pool, ele_n, ele_size, NULL);
|
|
}
|
|
|
|
/*
|
|
* int ks_pool_free
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Free an address from a memory pool.
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* Success - KS_STATUS_SUCCESS
|
|
*
|
|
* Failure - ks_status_t error code
|
|
*
|
|
* ARGUMENTS:
|
|
*
|
|
* pool -> Pointer to the memory pool.
|
|
*
|
|
* addr <-> Pointer to pointer of Address to free.
|
|
*
|
|
*/
|
|
KS_DECLARE(ks_status_t) ks_pool_free_ex(ks_pool_t *pool, void **addrP)
|
|
{
|
|
ks_status_t ret = KS_STATUS_SUCCESS;
|
|
void *addr;
|
|
|
|
ks_assert(pool);
|
|
ks_assert(addrP);
|
|
ks_assert(*addrP);
|
|
|
|
addr = *addrP;
|
|
|
|
if ((ret = check_pool(pool)) != KS_STATUS_SUCCESS) goto done;
|
|
|
|
ks_mutex_lock(pool->mutex);
|
|
|
|
if (pool->log_func != NULL) {
|
|
ks_pool_prefix_t *prefix = (ks_pool_prefix_t *)((uintptr_t)addr - KS_POOL_PREFIX_SIZE);
|
|
// @todo check_prefix()?
|
|
pool->log_func(pool, prefix->refs == 1 ? KS_POOL_FUNC_FREE : KS_POOL_FUNC_DECREF, prefix->size, prefix->refs - 1, addr, NULL, 0);
|
|
}
|
|
|
|
ret = free_mem(pool, addr);
|
|
ks_mutex_unlock(pool->mutex);
|
|
|
|
done:
|
|
if (ret != KS_STATUS_REFS_EXIST) {
|
|
ks_assert(ret == KS_STATUS_SUCCESS);
|
|
*addrP = NULL;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* void *ks_pool_ref_ex
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Ref count increment an address in a memoory pool.
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* Success - The same pointer
|
|
*
|
|
* Failure - NULL
|
|
*
|
|
* ARGUMENTS:
|
|
*
|
|
* pool -> Pointer to the memory pool.
|
|
*
|
|
* addr -> The addr to ref
|
|
*
|
|
* error_p <- Pointer to integer which, if not NULL, will be set with
|
|
* a ks_pool error code.
|
|
*/
|
|
KS_DECLARE(void *) ks_pool_ref_ex(ks_pool_t *pool, void *addr, ks_status_t *error_p)
|
|
{
|
|
ks_status_t ret = KS_STATUS_SUCCESS;
|
|
ks_pool_prefix_t *prefix;
|
|
ks_size_t refs;
|
|
|
|
ks_assert(pool);
|
|
ks_assert(addr);
|
|
|
|
if ((ret = check_pool(pool)) != KS_STATUS_SUCCESS) goto done;
|
|
|
|
prefix = (ks_pool_prefix_t *)((uintptr_t)addr - KS_POOL_PREFIX_SIZE);
|
|
if ((ret = check_prefix(prefix)) != KS_STATUS_SUCCESS) goto done;
|
|
|
|
ks_mutex_lock(pool->mutex);
|
|
refs = ++prefix->refs;
|
|
ks_mutex_unlock(pool->mutex);
|
|
|
|
if (pool->log_func != NULL) {
|
|
pool->log_func(pool, KS_POOL_FUNC_INCREF, prefix->size, refs, addr, NULL, 0);
|
|
}
|
|
|
|
done:
|
|
ks_assert(ret == KS_STATUS_SUCCESS);
|
|
|
|
return addr;
|
|
}
|
|
|
|
/*
|
|
* void *ks_pool_resize_ex
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Reallocate an address in a memory pool to a new size. This is
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* Success - Pointer to the address to use.
|
|
*
|
|
* Failure - NULL
|
|
*
|
|
* ARGUMENTS:
|
|
*
|
|
* pool -> Pointer to the memory pool.
|
|
*
|
|
*
|
|
* old_addr -> Previously allocated address.
|
|
*
|
|
* new_size -> New size of the allocation.
|
|
*
|
|
* error_p <- Pointer to integer which, if not NULL, will be set with
|
|
* a ks_pool error code.
|
|
*/
|
|
KS_DECLARE(void *) ks_pool_resize_ex(ks_pool_t *pool, void *old_addr, const ks_size_t new_size, ks_status_t *error_p)
|
|
{
|
|
ks_status_t ret = KS_STATUS_SUCCESS;
|
|
ks_size_t old_size;
|
|
ks_pool_prefix_t *prefix;
|
|
void *new_addr = NULL;
|
|
ks_size_t required;
|
|
|
|
ks_assert(pool);
|
|
ks_assert(new_size);
|
|
|
|
if ((ret = check_pool(pool)) != KS_STATUS_SUCCESS) {
|
|
SET_POINTER(error_p, ret);
|
|
return NULL;
|
|
}
|
|
|
|
if (!old_addr) {
|
|
return ks_pool_alloc_ex(pool, new_size, error_p);
|
|
}
|
|
|
|
prefix = (ks_pool_prefix_t *)((uintptr_t)old_addr - KS_POOL_PREFIX_SIZE);
|
|
if ((ret = check_prefix(prefix)) != KS_STATUS_SUCCESS) {
|
|
SET_POINTER(error_p, ret);
|
|
return NULL;
|
|
}
|
|
|
|
ks_mutex_lock(pool->mutex);
|
|
|
|
if (prefix->refs > 1) {
|
|
ret = KS_STATUS_NOT_ALLOWED;
|
|
goto done;
|
|
}
|
|
if (new_size == prefix->size) {
|
|
new_addr = old_addr;
|
|
goto done;
|
|
}
|
|
|
|
old_size = prefix->size;
|
|
|
|
required = KS_POOL_PREFIX_SIZE + new_size + KS_POOL_FENCE_SIZE;
|
|
new_addr = realloc((void *)prefix, required);
|
|
ks_assert(new_addr);
|
|
|
|
prefix = (ks_pool_prefix_t *)new_addr;
|
|
|
|
prefix->size = new_size;
|
|
|
|
new_addr = (void *)((uintptr_t)new_addr + KS_POOL_PREFIX_SIZE);
|
|
write_fence((void *)((uintptr_t)new_addr + new_size));
|
|
|
|
if (prefix->prev) prefix->prev->next = prefix;
|
|
else pool->first = prefix;
|
|
if (prefix->next) prefix->next->prev = prefix;
|
|
else pool->last = prefix;
|
|
|
|
if (pool->log_func != NULL) {
|
|
pool->log_func(pool, KS_POOL_FUNC_RESIZE, new_size, 0, old_addr, new_addr, old_size);
|
|
}
|
|
|
|
done:
|
|
ks_mutex_unlock(pool->mutex);
|
|
|
|
ks_assert(ret == KS_STATUS_SUCCESS);
|
|
|
|
return new_addr;
|
|
}
|
|
|
|
/*
|
|
* void *ks_pool_resize
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Reallocate an address in a mmeory pool to a new size. This is
|
|
* different from realloc in that it needs the old address' size.
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* Success - Pointer to the address to use.
|
|
*
|
|
* Failure - NULL
|
|
*
|
|
* ARGUMENTS:
|
|
*
|
|
* pool -> Pointer to the memory pool.
|
|
*
|
|
*
|
|
* old_addr -> Previously allocated address.
|
|
*
|
|
* new_size -> New size of the allocation.
|
|
*
|
|
*/
|
|
KS_DECLARE(void *) ks_pool_resize(ks_pool_t *pool, void *old_addr, const ks_size_t new_size)
|
|
{
|
|
return ks_pool_resize_ex(pool, old_addr, new_size, NULL);
|
|
}
|
|
|
|
/*
|
|
* int ks_pool_stats
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Return stats from the memory pool.
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* Success - KS_STATUS_SUCCESS
|
|
*
|
|
* Failure - ks_status_t error code
|
|
*
|
|
* ARGUMENTS:
|
|
*
|
|
* pool -> Pointer to the memory pool.
|
|
*
|
|
* num_alloced_p <- Pointer to an unsigned long which, if not NULL,
|
|
* will be set to the number of pointers currently allocated in pool.
|
|
*
|
|
* user_alloced_p <- Pointer to an unsigned long which, if not NULL,
|
|
* will be set to the number of user bytes allocated in this pool.
|
|
*
|
|
* max_alloced_p <- Pointer to an unsigned long which, if not NULL,
|
|
* will be set to the maximum number of user bytes that have been
|
|
* allocated in this pool.
|
|
*
|
|
* tot_alloced_p <- Pointer to an unsigned long which, if not NULL,
|
|
* will be set to the total amount of space (including administrative
|
|
* overhead) used by the pool.
|
|
*/
|
|
KS_DECLARE(ks_status_t) ks_pool_stats(const ks_pool_t *pool, ks_size_t *num_alloced_p, ks_size_t *user_alloced_p, ks_size_t *max_alloced_p, ks_size_t *tot_alloced_p)
|
|
{
|
|
ks_status_t ret = KS_STATUS_SUCCESS;
|
|
|
|
ks_assert(pool);
|
|
|
|
if ((ret = check_pool(pool)) != KS_STATUS_SUCCESS) goto done;
|
|
|
|
SET_POINTER(num_alloced_p, pool->alloc_c);
|
|
SET_POINTER(user_alloced_p, pool->user_alloc);
|
|
SET_POINTER(max_alloced_p, pool->max_alloc);
|
|
SET_POINTER(tot_alloced_p, pool->user_alloc + (pool->alloc_c * (KS_POOL_PREFIX_SIZE + KS_POOL_FENCE_SIZE)));
|
|
|
|
done:
|
|
ks_assert(ret == KS_STATUS_SUCCESS);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* int ks_pool_set_log_func
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Set a logging callback function to be called whenever there was a
|
|
* memory transaction. See ks_pool_log_func_t.
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* Success - KS_STATUS_SUCCESS
|
|
*
|
|
* Failure - ks_status_t error code
|
|
*
|
|
* ARGUMENTS:
|
|
*
|
|
* pool -> Pointer to the memory pool.
|
|
*
|
|
* log_func -> Log function (defined in ks_pool.h) which will be called
|
|
* with each ks_pool transaction.
|
|
*/
|
|
KS_DECLARE(ks_status_t) ks_pool_set_log_func(ks_pool_t *pool, ks_pool_log_func_t log_func)
|
|
{
|
|
ks_status_t ret = KS_STATUS_SUCCESS;
|
|
|
|
ks_assert(pool);
|
|
ks_assert(log_func);
|
|
|
|
if ((ret = check_pool(pool)) != KS_STATUS_SUCCESS) goto done;
|
|
|
|
pool->log_func = log_func;
|
|
|
|
done:
|
|
ks_assert(ret == KS_STATUS_SUCCESS);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* const char *ks_pool_strerror
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Return the corresponding string for the error number.
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* Success - String equivalient of the error.
|
|
*
|
|
* Failure - String "invalid error code"
|
|
*
|
|
* ARGUMENTS:
|
|
*
|
|
* error -> ks_status_t that we are converting.
|
|
*/
|
|
KS_DECLARE(const char *) ks_pool_strerror(const ks_status_t error)
|
|
{
|
|
switch (error) {
|
|
case KS_STATUS_SUCCESS:
|
|
return "no error";
|
|
break;
|
|
case KS_STATUS_ARG_NULL:
|
|
return "function argument is null";
|
|
break;
|
|
case KS_STATUS_ARG_INVALID:
|
|
return "function argument is invalid";
|
|
break;
|
|
case KS_STATUS_PNT:
|
|
return "invalid ks_pool pointer";
|
|
break;
|
|
case KS_STATUS_POOL_OVER:
|
|
return "ks_pool structure was overwritten";
|
|
break;
|
|
case KS_STATUS_PAGE_SIZE:
|
|
return "could not get system page-size";
|
|
break;
|
|
case KS_STATUS_OPEN_ZERO:
|
|
return "could not open /dev/zero";
|
|
break;
|
|
case KS_STATUS_NO_MEM:
|
|
return "no memory available";
|
|
break;
|
|
case KS_STATUS_SIZE:
|
|
return "error processing requested size";
|
|
break;
|
|
case KS_STATUS_TOO_BIG:
|
|
return "allocation exceeds pool max size";
|
|
break;
|
|
case KS_STATUS_MEM:
|
|
return "invalid memory address";
|
|
break;
|
|
case KS_STATUS_MEM_OVER:
|
|
return "memory lower bounds overwritten";
|
|
break;
|
|
case KS_STATUS_NOT_FOUND:
|
|
return "memory block not found in pool";
|
|
break;
|
|
case KS_STATUS_IS_FREE:
|
|
return "memory address has already been freed";
|
|
break;
|
|
case KS_STATUS_BLOCK_STAT:
|
|
return "invalid internal block status";
|
|
break;
|
|
case KS_STATUS_FREE_ADDR:
|
|
return "invalid internal free address";
|
|
break;
|
|
case KS_STATUS_NO_PAGES:
|
|
return "no available pages left in pool";
|
|
break;
|
|
case KS_STATUS_ALLOC:
|
|
return "system alloc function failed";
|
|
break;
|
|
case KS_STATUS_PNT_OVER:
|
|
return "user pointer admin space overwritten";
|
|
break;
|
|
case KS_STATUS_INVALID_POINTER:
|
|
return "pointer is not valid";
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return "invalid error code";
|
|
}
|
|
|
|
KS_DECLARE(char *) ks_pstrdup(ks_pool_t *pool, const char *str)
|
|
{
|
|
char *result;
|
|
ks_size_t len;
|
|
|
|
if (!str) {
|
|
return NULL;
|
|
}
|
|
|
|
len = (ks_size_t)strlen(str) + 1;
|
|
result = ks_pool_alloc(pool, len);
|
|
memcpy(result, str, len);
|
|
|
|
return result;
|
|
}
|
|
|
|
KS_DECLARE(char *) ks_pstrndup(ks_pool_t *pool, const char *str, ks_size_t len)
|
|
{
|
|
char *result;
|
|
const char *end;
|
|
|
|
if (!str) {
|
|
return NULL;
|
|
}
|
|
|
|
end = memchr(str, '\0', len);
|
|
|
|
if (!end) {
|
|
len = end - str;
|
|
}
|
|
|
|
result = ks_pool_alloc(pool, len + 1);
|
|
memcpy(result, str, len);
|
|
result[len] = '\0';
|
|
|
|
return result;
|
|
}
|
|
|
|
KS_DECLARE(char *) ks_pstrmemdup(ks_pool_t *pool, const char *str, ks_size_t len)
|
|
{
|
|
char *result;
|
|
|
|
if (!str) {
|
|
return NULL;
|
|
}
|
|
|
|
result = ks_pool_alloc(pool, len + 1);
|
|
memcpy(result, str, len);
|
|
result[len] = '\0';
|
|
|
|
return result;
|
|
}
|
|
|
|
KS_DECLARE(void *) ks_pmemdup(ks_pool_t *pool, const void *buf, ks_size_t len)
|
|
{
|
|
void *result;
|
|
|
|
if (!buf) {
|
|
return NULL;
|
|
}
|
|
|
|
result = ks_pool_alloc(pool, len);
|
|
memcpy(result, buf, len);
|
|
|
|
return result;
|
|
}
|
|
|
|
KS_DECLARE(char *) ks_pstrcat(ks_pool_t *pool, ...)
|
|
{
|
|
char *endp, *argp;
|
|
char *result;
|
|
ks_size_t lengths[10];
|
|
int i = 0;
|
|
ks_size_t len = 0;
|
|
va_list ap;
|
|
|
|
va_start(ap, pool);
|
|
|
|
/* get lengths so we know what to allocate, cache some so we don't have to double strlen those */
|
|
|
|
while ((argp = va_arg(ap, char *))) {
|
|
ks_size_t arglen = strlen(argp);
|
|
if (i < 10) lengths[i++] = arglen;
|
|
len += arglen;
|
|
}
|
|
|
|
va_end(ap);
|
|
|
|
result = (char *) ks_pool_alloc(pool, len + 1);
|
|
endp = result;
|
|
|
|
va_start(ap, pool);
|
|
|
|
i = 0;
|
|
|
|
while ((argp = va_arg(ap, char *))) {
|
|
len = (i < 10) ? lengths[i++] : strlen(argp);
|
|
memcpy(endp, argp, len);
|
|
endp += len;
|
|
}
|
|
|
|
va_end(ap);
|
|
|
|
*endp = '\0';
|
|
|
|
return result;
|
|
}
|
|
|
|
KS_DECLARE(char *) ks_psprintf(ks_pool_t *pool, const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
char *result;
|
|
va_start(ap, fmt);
|
|
result = ks_vpprintf(pool, fmt, ap);
|
|
va_end(ap);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
/* For Emacs:
|
|
* Local Variables:
|
|
* mode:c
|
|
* indent-tabs-mode:t
|
|
* tab-width:4
|
|
* c-basic-offset:4
|
|
* End:
|
|
* For VIM:
|
|
* vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
|
|
*/
|