]> source.dussan.org Git - rspamd.git/commitdiff
* Add support of shared memory chunks to memory pool allocator. Also add locking...
authorVsevolod Stakhov <vsevolod@rambler-co.ru>
Thu, 16 Oct 2008 15:41:45 +0000 (19:41 +0400)
committerVsevolod Stakhov <vsevolod@rambler-co.ru>
Thu, 16 Oct 2008 15:41:45 +0000 (19:41 +0400)
* Add simple test case for shared memory allocation

configure
mem_pool.c
mem_pool.h
test/rspamd_mem_pool_test.c

index d7fa8cf79499dfeccf160ba78a40305c8164212d..a34153948268dd280f5a0a920242c6ecbd2796d6 100755 (executable)
--- 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
index 00986c86965ae73a1a7091417e9bcca770fa7dca..f720138dcd30b9fbf79fb1e1919c02768d38b213 100644 (file)
@@ -2,8 +2,23 @@
 #include <glib.h>
 #include <string.h>
 #include <stdlib.h>
+#include <sys/mman.h>
+#include <errno.h>
+#include "config.h"
+
+#ifdef HAVE_SCHED_YIELD
+#include <sched.h>
+#endif
+
+#ifdef HAVE_NANOSLEEP
+#include <time.h>
+#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;
 }
 
index d20a5edcd396fda0d2b51be01b3a13053ea65433..70678e9fc761dd7297132619a5a67cdca58d9db0 100644 (file)
@@ -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);
index 50e539cf73220c01c0884a71d77c93658bf2877e..97ee03604150031d5b8633834bd6a51e9674635b 100644 (file)
@@ -2,6 +2,8 @@
 #include "tests.h"
 
 #include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
 #include <glib.h>
 
 #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);
 }