From 802890f0f2619ff93f543d1fcf55831cb647b174 Mon Sep 17 00:00:00 2001 From: Vsevolod Stakhov Date: Thu, 16 Oct 2008 19:41:45 +0400 Subject: [PATCH] * Add support of shared memory chunks to memory pool allocator. Also add locking support (spin mutexes) * Add simple test case for shared memory allocation --- configure | 29 ++++++ mem_pool.c | 172 ++++++++++++++++++++++++++++++++++++ mem_pool.h | 15 ++++ test/rspamd_mem_pool_test.c | 28 +++++- 4 files changed, 241 insertions(+), 3 deletions(-) diff --git a/configure b/configure index d7fa8cf79..a34153948 100755 --- a/configure +++ b/configure @@ -756,6 +756,17 @@ if [ $? -eq 0 ] ; then have_opt "SRANDOMDEV" fi +check_function "sched_yield" "sched.h" +if [ $? -eq 0 ] ; then + have_opt "SCHED_YIELD" +fi + +check_function "nanosleep" "time.h" +if [ $? -eq 0 ] ; then + have_opt "NANOSLEEP" +fi + + check_function "getpagesize" "unistd.h" if [ $? -eq 0 ] ; then have_opt "GETPAGESIZE" @@ -802,6 +813,24 @@ else have_opt "PATH_MAX" fi +check_macro "MAP_SHARED" "sys/mman.h" +if [ $? -eq 1 ] ; then + echo "Shared memory extension to mmap not found, this platform is not supported" + exit 1 +fi + +check_macro "MAP_ANON" "sys/mman.h" +if [ $? -eq 0 ] ; then + have_opt "MMAP_ANON" +else + if [ -f "/dev/zero" ] ; then + have_opt "MMAP_ZERO" + else + echo "Anon memory extension to mmap not found and /dev/zero not exists, this platform is not supported" + exit 1 + fi +fi + check_package "glib-2.0" "glib.h" check_package "gmime-2.0" "gmime/gmime.h" if [ $? -eq 1 ] ; then diff --git a/mem_pool.c b/mem_pool.c index 00986c869..f720138dc 100644 --- a/mem_pool.c +++ b/mem_pool.c @@ -2,8 +2,23 @@ #include #include #include +#include +#include +#include "config.h" + +#ifdef HAVE_SCHED_YIELD +#include +#endif + +#ifdef HAVE_NANOSLEEP +#include +#endif + #include "mem_pool.h" +/* Sleep time for spin lock in nanoseconds */ +#define MUTEX_SLEEP_TIME 10000000L + #ifdef _THREAD_SAFE pthread_mutex_t stat_mtx = PTHREAD_MUTEX_INITIALIZER; #define STAT_LOCK() do { pthread_mutex_lock (&stat_mtx); } while (0) @@ -27,6 +42,7 @@ pthread_mutex_t stat_mtx = PTHREAD_MUTEX_INITIALIZER; static size_t bytes_allocated = 0; static size_t chunks_allocated = 0; static size_t chunks_freed = 0; +static size_t shared_chunks_allocated = 0; static struct _pool_chain * pool_chain_new (size_t size) @@ -44,6 +60,43 @@ pool_chain_new (size_t size) return chain; } +static struct _pool_chain_shared * +pool_chain_new_shared (size_t size) +{ + struct _pool_chain_shared *chain; + +#if defined(HAVE_MMAP_ANON) + chain = mmap (NULL, size + sizeof (struct _pool_chain_shared), PROT_READ|PROT_WRITE, MAP_ANON|MAP_SHARED, -1, 0); + chain->begin = ((u_char *)chain) + sizeof (struct _pool_chain_shared); + if (chain == MAP_FAILED) { + return NULL; + } +#elif defined(HAVE_MMAP_ZERO) + int fd; + + fd = open ("/dev/zero", O_RDWR); + if (fd == -1) { + return NULL; + } + chain = mmap (NULL, shm->size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + chain->begin = ((u_char *)chain) + sizeof (struct _pool_chain_shared); + if (chain == MAP_FAILED) { + return NULL; + } +#else +# error No mmap methods are defined +#endif + chain->len = size; + chain->pos = chain->begin; + chain->lock = 0; + chain->next = NULL; + STAT_LOCK (); + shared_chunks_allocated ++; + STAT_UNLOCK (); + + return chain; +} + memory_pool_t* memory_pool_new (size_t size) { @@ -51,6 +104,7 @@ memory_pool_new (size_t size) new = g_malloc (sizeof (memory_pool_t)); new->cur_pool = pool_chain_new (size); + new->shared_pool = NULL; new->first_pool = new->cur_pool; new->destructors = NULL; @@ -126,6 +180,112 @@ memory_pool_strdup (memory_pool_t *pool, const char *src) return newstr; } +void * +memory_pool_alloc_shared (memory_pool_t *pool, size_t size) +{ + u_char *tmp; + struct _pool_chain_shared *new, *cur; + + if (pool) { + cur = pool->shared_pool; + if (!cur) { + cur = pool_chain_new_shared (pool->first_pool->len); + pool->shared_pool = cur; + } + + /* Find free space in pool chain */ + while (memory_pool_free (cur) < size && cur->next) { + cur = cur->next; + } + if (cur->next == NULL && memory_pool_free (cur) < size) { + /* Allocate new pool */ + if (cur->len >= size) { + new = pool_chain_new_shared (cur->len); + } + else { + new = pool_chain_new_shared (size + cur->len); + } + /* Attach new pool to chain */ + cur->next = new; + new->pos += size; + STAT_LOCK (); + bytes_allocated += size; + STAT_UNLOCK (); + return new->begin; + } + tmp = cur->pos; + cur->pos += size; + STAT_LOCK (); + bytes_allocated += size; + STAT_UNLOCK (); + return tmp; + } + return NULL; +} + +/* Find pool for a pointer, returns NULL if pointer is not in pool */ +static struct _pool_chain_shared * +memory_pool_find_pool (memory_pool_t *pool, void *pointer) +{ + struct _pool_chain_shared *cur = pool->shared_pool; + + while (cur) { + if ((u_char *)pointer >= cur->begin && (u_char *)pointer <= (cur->begin + cur->len)) { + return cur; + } + cur = cur->next; + } + + return NULL; +} + +static void +memory_pool_spin (struct _pool_chain_shared *chain) +{ + while (!g_atomic_int_compare_and_exchange (&chain->lock, 0, 1)) { + /* lock was aqquired */ +#ifdef HAVE_NANOSLEEP + struct timespec ts; + ts.tv_sec = 0; + ts.tv_nsec = MUTEX_SLEEP_TIME; + /* Spin */ + while (nanosleep (&ts, &ts) == -1 && errno == EINTR); +#endif +#ifdef HAVE_SCHED_YIELD + (void)sched_yield (); +#endif +#if !defined(HAVE_NANOSLEEP) && !defined(HAVE_SCHED_YIELD) +# error No methods to spin are defined +#endif + } +} + +/* Simple implementation of spinlock */ +void +memory_pool_lock_shared (memory_pool_t *pool, void *pointer) +{ + struct _pool_chain_shared *chain; + + chain = memory_pool_find_pool (pool, pointer); + if (chain == NULL) { + return; + } + + memory_pool_spin (chain); +} + +void memory_pool_unlock_shared (memory_pool_t *pool, void *pointer) +{ + struct _pool_chain_shared *chain; + + chain = memory_pool_find_pool (pool, pointer); + if (chain == NULL) { + return; + } + + (void)g_atomic_int_dec_and_test (&chain->lock); +} + void memory_pool_add_destructor (memory_pool_t *pool, pool_destruct_func func, void *data) { @@ -144,6 +304,7 @@ void memory_pool_delete (memory_pool_t *pool) { struct _pool_chain *cur = pool->first_pool, *tmp; + struct _pool_chain_shared *cur_shared = pool->shared_pool, *tmp_shared; struct _pool_destructors *destructor = pool->destructors; /* Call all pool destructors */ @@ -161,6 +322,16 @@ memory_pool_delete (memory_pool_t *pool) chunks_freed ++; STAT_UNLOCK (); } + /* Unmap shared memory */ + while (cur_shared) { + tmp_shared = cur_shared; + cur_shared = cur_shared->next; + munmap (tmp_shared, tmp_shared->len + sizeof (struct _pool_chain_shared)); + STAT_LOCK (); + chunks_freed ++; + STAT_UNLOCK (); + } + g_free (pool); } @@ -169,6 +340,7 @@ memory_pool_stat (memory_pool_stat_t *st) { st->bytes_allocated = bytes_allocated; st->chunks_allocated = chunks_allocated; + st->shared_chunks_allocated = shared_chunks_allocated; st->chunks_freed = chunks_freed; } diff --git a/mem_pool.h b/mem_pool.h index d20a5edcd..70678e9fc 100644 --- a/mem_pool.h +++ b/mem_pool.h @@ -12,20 +12,32 @@ struct _pool_chain { size_t len; struct _pool_chain *next; }; + +struct _pool_chain_shared { + u_char *begin; + u_char *pos; + size_t len; + gint lock; + struct _pool_chain_shared *next; +}; + struct _pool_destructors { pool_destruct_func func; void *data; struct _pool_destructors *prev; }; + typedef struct memory_pool_s { struct _pool_chain *cur_pool; struct _pool_chain *first_pool; + struct _pool_chain_shared *shared_pool; struct _pool_destructors *destructors; } memory_pool_t; typedef struct memory_pool_stat_s { size_t bytes_allocated; size_t chunks_allocated; + size_t shared_chunks_allocated; size_t chunks_freed; } memory_pool_stat_t; @@ -34,6 +46,9 @@ void* memory_pool_alloc (memory_pool_t* pool, size_t size); void* memory_pool_alloc0 (memory_pool_t* pool, size_t size); char* memory_pool_strdup (memory_pool_t* pool, const char *src); void memory_pool_add_destructor (memory_pool_t *pool, pool_destruct_func func, void *data); +void* memory_pool_alloc_shared (memory_pool_t *pool, size_t size); +void memory_pool_lock_shared (memory_pool_t *pool, void *pointer); +void memory_pool_unlock_shared (memory_pool_t *pool, void *pointer); void memory_pool_delete (memory_pool_t* pool); void memory_pool_stat (memory_pool_stat_t *st); diff --git a/test/rspamd_mem_pool_test.c b/test/rspamd_mem_pool_test.c index 50e539cf7..97ee03604 100644 --- a/test/rspamd_mem_pool_test.c +++ b/test/rspamd_mem_pool_test.c @@ -2,6 +2,8 @@ #include "tests.h" #include +#include +#include #include #define TEST_BUF "test bufffer" @@ -12,23 +14,43 @@ rspamd_mem_pool_test_func () { memory_pool_t *pool; memory_pool_stat_t st; - char *tmp, *tmp2; + char *tmp, *tmp2, *tmp3; + pid_t pid; + int ret; pool = memory_pool_new (sizeof (TEST_BUF)); tmp = memory_pool_alloc (pool, sizeof (TEST_BUF)); tmp2 = memory_pool_alloc (pool, sizeof (TEST_BUF) * 2); + tmp3 = memory_pool_alloc_shared (pool, sizeof (TEST_BUF)); snprintf (tmp, sizeof (TEST_BUF), "%s", TEST_BUF); snprintf (tmp2, sizeof (TEST_BUF) * 2, "%s", TEST2_BUF); + snprintf (tmp3, sizeof (TEST_BUF), "%s", TEST_BUF); g_assert (strncmp (tmp, TEST_BUF, sizeof (TEST_BUF)) == 0); g_assert (strncmp (tmp2, TEST2_BUF, sizeof (TEST2_BUF)) == 0); + g_assert (strncmp (tmp3, TEST_BUF, sizeof (TEST_BUF)) == 0); + memory_pool_lock_shared (pool, tmp3); + if ((pid = fork ()) == 0) { + memory_pool_lock_shared (pool, tmp3); + g_assert (*tmp3 == 's'); + *tmp3 = 't'; + memory_pool_unlock_shared (pool, tmp3); + exit (EXIT_SUCCESS); + } + else { + *tmp3 = 's'; + memory_pool_unlock_shared (pool, tmp3); + } + wait (&ret); + g_assert (*tmp3 == 't'); memory_pool_delete (pool); memory_pool_stat (&st); /* Check allocator stat */ - g_assert (st.bytes_allocated == sizeof (TEST_BUF) * 3); + g_assert (st.bytes_allocated == sizeof (TEST_BUF) * 4); g_assert (st.chunks_allocated == 2); - g_assert (st.chunks_freed == 2); + g_assert (st.shared_chunks_allocated == 1); + g_assert (st.chunks_freed == 3); } -- 2.39.5