From 8ddf071a484f2c44ea0e2043ac0e09e3e8538182 Mon Sep 17 00:00:00 2001 From: Alex Schneider Date: Wed, 6 Feb 2019 19:10:28 +0100 Subject: [PATCH] Implementaion of optional thread safety --- README.md | 1 + src/mpz_alloc.c | 148 +++++++++++++++++++++++++++++++----------------- src/mpz_alloc.h | 34 ++++++++++- src/mpz_core.h | 52 ++++++++++------- src/mpz_mutex.h | 39 +++++++++++++ 5 files changed, 198 insertions(+), 76 deletions(-) create mode 100644 src/mpz_mutex.h diff --git a/README.md b/README.md index 41f45d7..d2f92b9 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ for structs in tokenization applications. ## Features * Written in pure `C`. +* Supports thread safety. * Doesn't require any external libraries. * Optimized for 32 and 64 bit systems on little-endian machines. * Prevents internal memory space fragmentation (freed memory space is reusable). diff --git a/src/mpz_alloc.c b/src/mpz_alloc.c index 04b2e81..1bca4a4 100644 --- a/src/mpz_alloc.c +++ b/src/mpz_alloc.c @@ -14,13 +14,10 @@ #define MPZ_ALLOC_ALIGNMENT (2 * sizeof(mpz_void_t *)) -#define MPZ_CHECK_VOID(p) ({ if (NULL == (p)) return; }) -#define MPZ_CHECK_NULL(p) ({ if (NULL == (p)) return NULL; }) - #ifdef MPZ_RAISE_SIGSEGV_ON_MEM_ERRORS -# define MPZ_SLOT_OVERHEAD (sizeof(mpz_uint32_t) * 2) + #define MPZ_SLOT_OVERHEAD (sizeof(mpz_uint32_t) * 2) #else -# define MPZ_SLOT_OVERHEAD (sizeof(mpz_uint32_t) * 1) + #define MPZ_SLOT_OVERHEAD (sizeof(mpz_uint32_t) * 1) #endif /* MPZ_RAISE_SIGSEGV_ON_MEM_ERRORS */ #define MPZ_SLOT_SIZE (sizeof(mpz_slot_t *) + MPZ_SLOT_OVERHEAD) @@ -38,36 +35,25 @@ #define MPZ_POOL_MIN_ALLOC (MPZ_SLOTS_ALIGNMENT) #define MPZ_POOL_MAX_ALLOC ((mpz_cuint32_t)((1 << 29) - 1)) -#define MPZ_SLOT_READ_HEAD(s) ( \ +#define MPZ_SLOT_READ_HEAD(s) ( \ (mpz_uint32_t *)(s) \ ) -#define MPZ_SLOT_READ_SIZE(s) ( \ +#define MPZ_SLOT_READ_SIZE(s) ( \ (mpz_uint32_t)((*MPZ_SLOT_READ_HEAD(s) << 2) >> 2) \ ) #ifdef MPZ_RAISE_SIGSEGV_ON_MEM_ERRORS + #define MPZ_FOOTER_MARK (0xFFFFFFFF) -#define MPZ_FOOTER_MARK (0xFFFFFFFF) - -#define MPZ_SLOT_GOTO_FOOT(sl, si) ( \ - (mpz_uint32_t *)((mpz_uchar_t *)(sl) + sizeof(mpz_uint32_t) + (si)) \ -) -#define MPZ_SLOT_READ_FOOT(s) ( \ - (mpz_uint32_t *)MPZ_SLOT_GOTO_FOOT((s), MPZ_SLOT_READ_SIZE(s)) \ -) + #define MPZ_SLOT_GOTO_FOOT(sl, si) ( \ + (mpz_uint32_t *)((mpz_uchar_t *)(sl) + sizeof(mpz_uint32_t) + (si)) \ + ) + #define MPZ_SLOT_READ_FOOT(s) ( \ + (mpz_uint32_t *)MPZ_SLOT_GOTO_FOOT((s), MPZ_SLOT_READ_SIZE(s)) \ + ) #endif /* MPZ_RAISE_SIGSEGV_ON_MEM_ERRORS */ -#if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */ -# ifdef __GNUC__ -# define MPZ_FORCE_INLINE static inline __attribute__((always_inline)) -# else -# define MPZ_FORCE_INLINE static inline -# endif /* __GNUC__ */ -#else -# define MPZ_FORCE_INLINE static -#endif /* __STDC_VERSION__ */ - /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ MPZ_FORCE_INLINE mpz_void_t _mpz_pool_gc( @@ -104,8 +90,8 @@ MPZ_FORCE_INLINE mpz_void_t _mpz_slot_init( mpz_pool_t *mpz_pool_create( mpz_void_t ) { - mpz_pool_t *pool; - mpz_uint_t idx; + mpz_pool_t *pool; + mpz_uint_t idx; MPZ_CHECK_NULL(pool = mpz_memalign(MPZ_ALLOC_ALIGNMENT, MPZ_POOL_SIZE)); @@ -115,21 +101,54 @@ mpz_pool_t *mpz_pool_create( pool->slabs = NULL; +#ifdef MPZ_ENABLE_THREAD_SAFETY + mpz_mutex_init(&pool->mutex, NULL); +#endif /* MPZ_ENABLE_THREAD_SAFETY */ + return pool; } -mpz_void_t mpz_pool_reset( +mpz_int_t mpz_pool_reset( mpz_pool_t *pool ) { +#ifdef MPZ_ENABLE_THREAD_SAFETY + MPZ_CHECK_INT(pool, MPZ_FAILURE); + + if (0 != mpz_mutex_lock(&pool->mutex)) { + return MPZ_FAILURE; + } +#endif /* MPZ_ENABLE_THREAD_SAFETY */ + _mpz_pool_gc(pool, 1); + +#ifdef MPZ_ENABLE_THREAD_SAFETY + mpz_mutex_unlock(&pool->mutex); +#endif /* MPZ_ENABLE_THREAD_SAFETY */ + + return MPZ_SUCCESS; } -mpz_void_t mpz_pool_destroy( +mpz_int_t mpz_pool_destroy( mpz_pool_t *pool ) { +#ifdef MPZ_ENABLE_THREAD_SAFETY + MPZ_CHECK_INT(pool, MPZ_FAILURE); + + if (0 != mpz_mutex_lock(&pool->mutex)) { + return MPZ_FAILURE; + } +#endif /* MPZ_ENABLE_THREAD_SAFETY */ + _mpz_pool_gc(pool, 0); +#ifdef MPZ_ENABLE_THREAD_SAFETY + mpz_mutex_unlock(&pool->mutex); + mpz_mutex_destroy(&pool->mutex); +#endif /* MPZ_ENABLE_THREAD_SAFETY */ + free(pool); + + return MPZ_SUCCESS; } mpz_void_t *mpz_pmalloc( @@ -144,30 +163,40 @@ mpz_void_t *mpz_pcalloc( return _mpz_palloc(pool, size, 1); } -mpz_void_t mpz_free( +mpz_int_t mpz_free( mpz_pool_t *pool, mpz_cvoid_t *data ) { - mpz_slot_t *slot; - mpz_uint32_t *head, size; - mpz_uint_t idx; + mpz_slot_t *slot; + mpz_uint32_t *head, size; + mpz_uint_t idx; - MPZ_CHECK_VOID(pool); - MPZ_CHECK_VOID(data); + MPZ_CHECK_INT(pool, MPZ_FAILURE); + MPZ_CHECK_INT(data, MPZ_FAILURE); + +#ifdef MPZ_ENABLE_THREAD_SAFETY + if (0 != mpz_mutex_lock(&pool->mutex)) { + return MPZ_FAILURE; + } +#endif /* MPZ_ENABLE_THREAD_SAFETY */ slot = MPZ_DATA_TO_SLOT(data); head = MPZ_SLOT_READ_HEAD(slot); #ifdef MPZ_RAISE_SIGSEGV_ON_MEM_ERRORS - /* Check both "segmentation faults" and "double free" errors. */ if ((MPZ_FOOTER_MARK != *MPZ_SLOT_READ_FOOT(slot)) || (!(*head & MPZ_SLOT_FLAG_USED))) { raise(SIGSEGV); } - #endif /* MPZ_RAISE_SIGSEGV_ON_MEM_ERRORS */ if (*head & MPZ_SLOT_FLAG_HUGE) { - return _mpz_slab_free(pool, slot); + _mpz_slab_free(pool, slot); + +#ifdef MPZ_ENABLE_THREAD_SAFETY + mpz_mutex_unlock(&pool->mutex); +#endif /* MPZ_ENABLE_THREAD_SAFETY */ + + return MPZ_SUCCESS; } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ @@ -175,10 +204,8 @@ mpz_void_t mpz_free( size = MPZ_SLOT_READ_SIZE(slot); #ifdef MPZ_RAISE_SIGSEGV_ON_MEM_ERRORS - /* Remove the "used" mark. */ _mpz_slot_init(slot, size, 0); - #endif /* MPZ_RAISE_SIGSEGV_ON_MEM_ERRORS */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ @@ -189,6 +216,12 @@ mpz_void_t mpz_free( slot->next = pool->bins[idx]; pool->bins[idx] = slot; + +#ifdef MPZ_ENABLE_THREAD_SAFETY + mpz_mutex_unlock(&pool->mutex); +#endif /* MPZ_ENABLE_THREAD_SAFETY */ + + return MPZ_SUCCESS; } /* ==================================================================================================== */ @@ -197,9 +230,9 @@ mpz_void_t mpz_free( MPZ_FORCE_INLINE mpz_void_t _mpz_pool_gc( mpz_pool_t *pool, mpz_cuint_t soft ) { - mpz_slab_t *slab, *next; - mpz_slot_t *slot; - mpz_uint_t idx; + mpz_slab_t *slab, *next; + mpz_slot_t *slot; + mpz_uint_t idx; MPZ_CHECK_VOID(pool); MPZ_CHECK_VOID(pool->slabs); @@ -240,9 +273,9 @@ MPZ_FORCE_INLINE mpz_void_t _mpz_pool_gc( MPZ_FORCE_INLINE mpz_void_t *_mpz_palloc( mpz_pool_t *pool, mpz_size_t size, mpz_cuint_t zeroize ) { - mpz_slab_t *slab; - mpz_slot_t *slot; - mpz_uint_t idx; + mpz_slab_t *slab; + mpz_slot_t *slot; + mpz_uint_t idx; MPZ_CHECK_NULL(pool); @@ -252,6 +285,12 @@ MPZ_FORCE_INLINE mpz_void_t *_mpz_palloc( return NULL; } +#ifdef MPZ_ENABLE_THREAD_SAFETY + if (0 != mpz_mutex_lock(&pool->mutex)) { + return NULL; + } +#endif /* MPZ_ENABLE_THREAD_SAFETY */ + size += MPZ_SLOT_OVERHEAD; size = mpz_align(size, MPZ_SLOTS_ALIGNMENT); @@ -264,6 +303,10 @@ MPZ_FORCE_INLINE mpz_void_t *_mpz_palloc( /* The new slab contains only a single huge slot. */ _mpz_slot_init(slot = MPZ_SLAB_TO_SLOT(slab), size, MPZ_SLOT_FLAG_HUGE|MPZ_SLOT_FLAG_USED); +#ifdef MPZ_ENABLE_THREAD_SAFETY + mpz_mutex_unlock(&pool->mutex); +#endif /* MPZ_ENABLE_THREAD_SAFETY */ + return MPZ_SLOT_TO_DATA(slot); } @@ -288,16 +331,18 @@ MPZ_FORCE_INLINE mpz_void_t *_mpz_palloc( pool->bins[idx] = slot->next; #ifdef MPZ_RAISE_SIGSEGV_ON_MEM_ERRORS - /* Mark the slot as "used". */ _mpz_slot_init(slot, size, MPZ_SLOT_FLAG_USED); - #endif /* MPZ_RAISE_SIGSEGV_ON_MEM_ERRORS */ if (zeroize) { mpz_memzero(MPZ_SLOT_TO_DATA(slot), size); } +#ifdef MPZ_ENABLE_THREAD_SAFETY + mpz_mutex_unlock(&pool->mutex); +#endif /* MPZ_ENABLE_THREAD_SAFETY */ + return MPZ_SLOT_TO_DATA(slot); } @@ -316,8 +361,8 @@ MPZ_FORCE_INLINE mpz_void_t *_mpz_slab_create( MPZ_FORCE_INLINE mpz_void_t _mpz_slab_init( mpz_pool_t *pool, mpz_slab_t *slab, mpz_cuint32_t size ) { - mpz_slot_t *slot = MPZ_SLAB_TO_SLOT(slab); - mpz_uint_t idx, i; + mpz_slot_t *slot = MPZ_SLAB_TO_SLOT(slab); + mpz_uint_t idx, i; idx = MPZ_BIN_IDX(size); @@ -392,9 +437,6 @@ MPZ_FORCE_INLINE mpz_void_t _mpz_slot_init( *MPZ_SLOT_READ_HEAD(slot) = (0 | flags | size); #ifdef MPZ_RAISE_SIGSEGV_ON_MEM_ERRORS - *MPZ_SLOT_GOTO_FOOT(slot, size) = MPZ_FOOTER_MARK; - #endif /* MPZ_RAISE_SIGSEGV_ON_MEM_ERRORS */ - } diff --git a/src/mpz_alloc.h b/src/mpz_alloc.h index 4977467..6350394 100644 --- a/src/mpz_alloc.h +++ b/src/mpz_alloc.h @@ -10,15 +10,43 @@ /* ==================================================================================================== */ +typedef struct _mpz_pool_s mpz_pool_t; +typedef struct _mpz_slab_s mpz_slab_t; +typedef struct _mpz_slot_s mpz_slot_t; + +struct _mpz_slot_s +{ + mpz_void_t *data; + mpz_slot_t *next; +}; + +struct _mpz_slab_s +{ + mpz_slab_t *prev; + mpz_slab_t *next; +}; + +struct _mpz_pool_s +{ + mpz_slot_t *bins[MPZ_BINS]; + mpz_slab_t *slabs; + +#ifdef MPZ_ENABLE_THREAD_SAFETY + mpz_mutex_t mutex; +#endif /* MPZ_ENABLE_THREAD_SAFETY */ +}; + +/* ==================================================================================================== */ + mpz_pool_t *mpz_pool_create( mpz_void_t ); -mpz_void_t mpz_pool_reset( +mpz_int_t mpz_pool_reset( mpz_pool_t *pool ); -mpz_void_t mpz_pool_destroy( +mpz_int_t mpz_pool_destroy( mpz_pool_t *pool ); @@ -30,7 +58,7 @@ mpz_void_t *mpz_pcalloc( mpz_pool_t *pool, mpz_csize_t size ); -mpz_void_t mpz_free( +mpz_int_t mpz_free( mpz_pool_t *pool, mpz_cvoid_t *data ); diff --git a/src/mpz_core.h b/src/mpz_core.h index 5978662..cdc4013 100755 --- a/src/mpz_core.h +++ b/src/mpz_core.h @@ -34,12 +34,20 @@ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +/** + * The MPZ supports thread safety and may easy be used in multi-thread applications. + * To disable this behavior just comment out the following definement. + * + * Disabling of this feature increases the performance of the MPZ. +*/ +#define MPZ_ENABLE_THREAD_SAFETY + /** * The MPZ implements simple checks for "segmentation faults" and "double free" * errors. In cases that an error is detected MPZ immediately raises an "SIGSEGV" * error. To disable this behavior just comment out the following definement. * - * Disabling of this feature increases a little bit the performance of the MPZ. + * Disabling of this feature increases the performance of the MPZ. */ #define MPZ_RAISE_SIGSEGV_ON_MEM_ERRORS @@ -92,6 +100,7 @@ typedef void mpz_void_t; typedef const void mpz_cvoid_t; typedef unsigned char mpz_uchar_t; +typedef int mpz_int_t; typedef unsigned int mpz_uint_t; typedef const unsigned int mpz_cuint_t; typedef uint32_t mpz_uint32_t; @@ -101,36 +110,39 @@ typedef const size_t mpz_csize_t; /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ -typedef struct _mpz_pool_s mpz_pool_t; -typedef struct _mpz_slab_s mpz_slab_t; -typedef struct _mpz_slot_s mpz_slot_t; +#if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */ + #ifdef __GNUC__ + #define MPZ_FORCE_INLINE static inline __attribute__((always_inline)) + #else + #define MPZ_FORCE_INLINE static inline + #endif /* __GNUC__ */ +#else + #define MPZ_FORCE_INLINE static +#endif /* __STDC_VERSION__ */ -struct _mpz_slot_s -{ - mpz_void_t *data; - mpz_slot_t *next; -}; +#define MPZ_CHECK_INT(p, i) ({ if (NULL == (p)) return (i); }) +#define MPZ_CHECK_VOID(p) ({ if (NULL == (p)) return; }) +#define MPZ_CHECK_NULL(p) ({ if (NULL == (p)) return NULL; }) -struct _mpz_slab_s -{ - mpz_slab_t *prev; - mpz_slab_t *next; -}; - -struct _mpz_pool_s -{ - mpz_slot_t *bins[MPZ_BINS]; - mpz_slab_t *slabs; -}; +#define MPZ_SUCCESS (0) +#define MPZ_FAILURE (1) /* ==================================================================================================== */ +#ifdef MPZ_ENABLE_THREAD_SAFETY +#include +#endif /* MPZ_ENABLE_THREAD_SAFETY */ + #ifdef MPZ_RAISE_SIGSEGV_ON_MEM_ERRORS #include #endif /* MPZ_RAISE_SIGSEGV_ON_MEM_ERRORS */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifdef MPZ_ENABLE_THREAD_SAFETY +#include "mpz_mutex.h" +#endif /* MPZ_ENABLE_THREAD_SAFETY */ + #include "mpz_alloc.h" /* ==================================================================================================== */ diff --git a/src/mpz_mutex.h b/src/mpz_mutex.h new file mode 100644 index 0000000..ef122c2 --- /dev/null +++ b/src/mpz_mutex.h @@ -0,0 +1,39 @@ + +/** + * Copyright (c) Alex Schneider + */ + +#ifndef _MEMPOOLZ_MUTEX_H_INCLUDE +#define _MEMPOOLZ_MUTEX_H_INCLUDE + +/* ==================================================================================================== */ + +#include "mpz_core.h" + +/* ==================================================================================================== */ + +#ifdef MPZ_ENABLE_THREAD_SAFETY +typedef pthread_mutex_t mpz_mutex_t; +#endif /* MPZ_ENABLE_THREAD_SAFETY */ + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#define mpz_mutex_init(m, a) ( \ + (mpz_int_t)pthread_mutex_init((m), (a)) \ +) + +#define mpz_mutex_lock(m) ( \ + (mpz_int_t)pthread_mutex_lock((m)) \ +) + +#define mpz_mutex_unlock(m) ( \ + (mpz_int_t)pthread_mutex_unlock((m)) \ +) + +#define mpz_mutex_destroy(m) ( \ + (mpz_int_t)pthread_mutex_destroy((m)) \ +) + +/* ==================================================================================================== */ + +#endif /* _SERVERZ_MUTEX_H_INCLUDE */