From 14a12f5fb733855c24a68b31ba34934e4395bc6b Mon Sep 17 00:00:00 2001 From: Pablo Rincon Date: Sun, 5 Sep 2010 23:46:39 +0200 Subject: [PATCH] Adding atomic bitwise operations api and rwlocks support --- src/threads.c | 53 ++++++++++++++++- src/threads.h | 144 ++++++++++++++++++++++++++++++++++++++++++++++ src/util-atomic.h | 130 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 324 insertions(+), 3 deletions(-) diff --git a/src/threads.c b/src/threads.c index ed671c06e0..a6124ee8d5 100644 --- a/src/threads.c +++ b/src/threads.c @@ -49,7 +49,7 @@ int ThreadMacrosTest01Mutex(void) { } /** - * \brief Test Spin Macros + * \brief Test Spinlock Macros * * Valgrind's DRD tool (valgrind-3.5.0-Debian) reports: * @@ -59,7 +59,7 @@ int ThreadMacrosTest01Mutex(void) { * ==31156== by 0x532E8A: UtRunTests (util-unittest.c:182) * ==31156== by 0x4065C3: main (suricata.c:789) * - * To me this is a false possitve, as the whole point of "trylock" is to see + * To me this is a false positve, as the whole point of "trylock" is to see * if a spinlock is actually locked. * */ @@ -75,6 +75,51 @@ int ThreadMacrosTest02Spinlocks(void) { return (r == 0)? 1 : 0; } +/** + * \brief Test RWLock macros + */ +int ThreadMacrosTest03RWLocks(void) { + SCRWLock rwl_write; + int r = 0; + r |= SCRWLockInit(&rwl_write, NULL); + r |= SCRWLockWRLock(&rwl_write); + r |= (SCRWLockTryWRLock(&rwl_write) == EBUSY)? 0 : 1; + r |= SCRWLockUnlock(&rwl_write); + r |= SCRWLockDestroy(&rwl_write); + + return (r == 0)? 1 : 0; +} + +/** + * \brief Test RWLock macros + */ +int ThreadMacrosTest04RWLocks(void) { + SCRWLock rwl_read; + int r = 0; + r |= SCRWLockInit(&rwl_read, NULL); + r |= SCRWLockRDLock(&rwl_read); + r |= (SCRWLockTryWRLock(&rwl_read) == EBUSY)? 0 : 1; + r |= SCRWLockUnlock(&rwl_read); + r |= SCRWLockDestroy(&rwl_read); + + return (r == 0)? 1 : 0; +} + +/** + * \brief Test RWLock macros + */ +int ThreadMacrosTest05RWLocks(void) { + SCRWLock rwl_read; + int r = 0; + r |= SCRWLockInit(&rwl_read, NULL); + r |= SCRWLockWRLock(&rwl_read); + r |= (SCRWLockTryRDLock(&rwl_read) == EBUSY)? 0 : 1; + r |= SCRWLockUnlock(&rwl_read); + r |= SCRWLockDestroy(&rwl_read); + + return (r == 0)? 1 : 0; +} + #endif /* UNIT TESTS */ /** @@ -84,6 +129,8 @@ void ThreadMacrosRegisterTests(void) { #ifdef UNITTESTS /* UNIT TESTS */ UtRegisterTest("ThreadMacrosTest01Mutex", ThreadMacrosTest01Mutex, 1); - UtRegisterTest("ThreadMacrossTest02Spinlocks", ThreadMacrosTest02Spinlocks, 1); + UtRegisterTest("ThreadMacrosTest02Spinlocks", ThreadMacrosTest02Spinlocks, 1); + UtRegisterTest("ThreadMacrosTest03RWLocks", ThreadMacrosTest03RWLocks, 1); + UtRegisterTest("ThreadMacrosTest04RWLocks", ThreadMacrosTest04RWLocks, 1); #endif /* UNIT TESTS */ } diff --git a/src/threads.h b/src/threads.h index 7b8d6d737c..5c6b75dd0a 100644 --- a/src/threads.h +++ b/src/threads.h @@ -79,6 +79,10 @@ #define SCMutexAttr pthread_mutexattr_t #define SCMutexDestroy pthread_mutex_destroy +/** Suricata RWLocks */ +#define SCRWLock pthread_rwlock_t +#define SCRWLockDestroy pthread_rwlock_destroy + /** Get the Current Thread Id */ #ifdef OS_FREEBSD #define SCGetThreadIdLong(...) ({ \ @@ -374,6 +378,146 @@ }) #endif + +/** RWLock Functions */ +#ifdef DBG_THREADS +/** When dbg threads is defined, if a rwlock fail to lock, it's + * initialized, logged, and does a second try; This is to prevent the system to freeze; + * If you see a rwlock, spinlock or condiion not initialized, report it please! + */ +#define SCRWLockRDLock_dbg(rwl) ({ \ + printf("%16s(%s:%d): (thread:%"PRIuMAX") locking rwlock %p\n", __FUNCTION__, __FILE__, __LINE__, (uintmax_t)pthread_self(), rwl); \ + int retl = pthread_rwlock_rdlock(rwl); \ + printf("%16s(%s:%d): (thread:%"PRIuMAX") locked rwlock %p ret %" PRId32 "\n", __FUNCTION__, __FILE__, __LINE__, (uintmax_t)pthread_self(), rwl, retl); \ + if (retl != 0) { \ + switch (retl) { \ + case EINVAL: \ + printf("The value specified by attr is invalid\n"); \ + retl = pthread_rwlock_init(rwl, NULL); \ + if (retl != 0) \ + exit(EXIT_FAILURE); \ + retl = pthread_rwlock_rdlock(rwl); \ + break; \ + case EDEADLK: \ + printf("A deadlock would occur if the thread blocked waiting for rwlock\n"); \ + break; \ + } \ + } \ + retl; \ +}) + +#define SCRWLockWRLock_dbg(rwl) ({ \ + printf("%16s(%s:%d): (thread:%"PRIuMAX") locking rwlock %p\n", __FUNCTION__, __FILE__, __LINE__, (uintmax_t)pthread_self(), rwl); \ + int retl = pthread_rwlock_wrlock(rwl); \ + printf("%16s(%s:%d): (thread:%"PRIuMAX") locked rwlock %p ret %" PRId32 "\n", __FUNCTION__, __FILE__, __LINE__, (uintmax_t)pthread_self(), rwl, retl); \ + if (retl != 0) { \ + switch (retl) { \ + case EINVAL: \ + printf("The value specified by attr is invalid\n"); \ + retl = pthread_rwlock_init(rwl, NULL); \ + if (retl != 0) \ + exit(EXIT_FAILURE); \ + retl = pthread_rwlock_wrlock(rwl); \ + break; \ + case EDEADLK: \ + printf("A deadlock would occur if the thread blocked waiting for rwlock\n"); \ + break; \ + } \ + } \ + retl; \ +}) + + +#define SCRWLockTryWRLock_dbg(rwl) ({ \ + printf("%16s(%s:%d): (thread:%"PRIuMAX") trylocking rwlock %p\n", __FUNCTION__, __FILE__, __LINE__, (uintmax_t)pthread_self(), rwl); \ + int rett = pthread_rwlock_trywrlock(rwl); \ + printf("%16s(%s:%d): (thread:%"PRIuMAX") trylocked rwlock %p ret %" PRId32 "\n", __FUNCTION__, __FILE__, __LINE__, (uintmax_t)pthread_self(), rwl, rett); \ + if (rett != 0) { \ + switch (rett) { \ + case EINVAL: \ + printf("%16s(%s:%d): The value specified by attr is invalid\n", __FUNCTION__, __FILE__, __LINE__); \ + break; \ + case EBUSY: \ + printf("RWLock is already locked\n"); \ + break; \ + } \ + } \ + rett; \ +}) + +#define SCRWLockTryRDLock_dbg(rwl) ({ \ + printf("%16s(%s:%d): (thread:%"PRIuMAX") trylocking rwlock %p\n", __FUNCTION__, __FILE__, __LINE__, (uintmax_t)pthread_self(), rwl); \ + int rett = pthread_rwlock_tryrdlock(rwl); \ + printf("%16s(%s:%d): (thread:%"PRIuMAX") trylocked rwlock %p ret %" PRId32 "\n", __FUNCTION__, __FILE__, __LINE__, (uintmax_t)pthread_self(), rwl, rett); \ + if (rett != 0) { \ + switch (rett) { \ + case EINVAL: \ + printf("%16s(%s:%d): The value specified by attr is invalid\n", __FUNCTION__, __FILE__, __LINE__); \ + break; \ + case EBUSY: \ + printf("RWLock is already locked\n"); \ + break; \ + } \ + } \ + rett; \ +}) + +#define SCRWLockInit_dbg(rwl, rwlattr) ({ \ + int ret; \ + ret = pthread_rwlock_init(rwl, rwlattr); \ + if (ret != 0) { \ + switch (ret) { \ + case EINVAL: \ + printf("The value specified by attr is invalid\n"); \ + printf("%16s(%s:%d): (thread:%"PRIuMAX") rwlock %p initialization returned %" PRId32 "\n", __FUNCTION__, __FILE__, __LINE__, (uintmax_t)pthread_self(), rwl, ret); \ + break; \ + case EAGAIN: \ + printf("The system temporarily lacks the resources to create another rwlock\n"); \ + printf("%16s(%s:%d): (thread:%"PRIuMAX") rwlock %p initialization returned %" PRId32 "\n", __FUNCTION__, __FILE__, __LINE__, (uintmax_t)pthread_self(), rwl, ret); \ + break; \ + case ENOMEM: \ + printf("The process cannot allocate enough memory to create another rwlock\n"); \ + printf("%16s(%s:%d): (thread:%"PRIuMAX") rwlock %p initialization returned %" PRId32 "\n", __FUNCTION__, __FILE__, __LINE__, (uintmax_t)pthread_self(), rwl, ret); \ + break; \ + } \ + } \ + ret; \ +}) + +#define SCRWLockUnlock_dbg(rwl) ({ \ + printf("%16s(%s:%d): (thread:%"PRIuMAX") unlocking rwlock %p\n", __FUNCTION__, __FILE__, __LINE__, (uintmax_t)pthread_self(), rwl); \ + int retu = pthread_rwlock_unlock(rwl); \ + printf("%16s(%s:%d): (thread:%"PRIuMAX") unlocked rwlock %p ret %" PRId32 "\n", __FUNCTION__, __FILE__, __LINE__, (uintmax_t)pthread_self(), rwl, retu); \ + if (retu != 0) { \ + switch (retu) { \ + case EINVAL: \ + printf("%16s(%s:%d): The value specified by attr is invalid\n", __FUNCTION__, __FILE__, __LINE__); \ + break; \ + case EPERM: \ + printf("The current thread does not hold a lock on rwlock\n"); \ + break; \ + } \ + } \ + retu; \ +}) + +#define SCRWLockInit(rwl, rwlattrs) SCRWLockInit_dbg(rwl, rwlattrs) +#define SCRWLockRDLock(rwl) SCRWLockRDLock_dbg(rwl) +#define SCRWLockWRLock(rwl) SCRWLockWRLock_dbg(rwl) +#define SCRWLockTryWRLock(rwl) SCRWLockTryWRLock_dbg(rwl) +#define SCRWLockTryRDLock(rwl) SCRWLockTryRDLock_dbg(rwl) +#define SCRWLockUnlock(rwl) SCRWLockUnlock_dbg(rwl) +#else +#define SCRWLockInit(rwl, rwlattr ) pthread_rwlock_init(rwl, rwlattr) +#define SCRWLockWRLock(rwl) pthread_rwlock_wrlock(rwl) +#define SCRWLockRDLock(rwl) pthread_rwlock_rdlock(rwl) +#define SCRWLockTryWRLock(rwl) pthread_rwlock_trywrlock(rwl) +#define SCRWLockTryRDLock(rwl) pthread_rwlock_tryrdlock(rwl) +#define SCRWLockUnlock(rwl) pthread_rwlock_unlock(rwl) +#endif + +/** End of RWLock functions */ + void ThreadMacrosRegisterTests(void); #endif /* __THREADS_H__ */ diff --git a/src/util-atomic.h b/src/util-atomic.h index 80d5d05946..1175d28b5a 100644 --- a/src/util-atomic.h +++ b/src/util-atomic.h @@ -19,6 +19,7 @@ * \file * * \author Victor Julien + * \author Pablo Rincon * * API for atomic operations. Uses atomic instructions (GCC only at this time) * where available, falls back to (spin)locked* operations otherwise. @@ -121,6 +122,58 @@ SCSpinUnlock(&(name ## _sc_lock__)); \ } while(0) +/** + * \brief Bitwise AND a value from our atomic variable + * + * \param name the atomic variable + * \param val the value to sub from the variable + */ +#define SC_ATOMIC_AND(name, val) \ + do { \ + SCSpinLock(&(name ## _sc_lock__)); \ + (name ## _sc_atomic__) &= (val); \ + SCSpinUnlock(&(name ## _sc_lock__)); \ + } while(0) + +/** + * \brief Bitwise OR a value from our atomic variable + * + * \param name the atomic variable + * \param val the value to sub from the variable + */ +#define SC_ATOMIC_OR(name, val) \ + do { \ + SCSpinLock(&(name ## _sc_lock__)); \ + (name ## _sc_atomic__) |= (val); \ + SCSpinUnlock(&(name ## _sc_lock__)); \ + } while(0) + +/** + * \brief Bitwise NAND a value from our atomic variable + * + * \param name the atomic variable + * \param val the value to sub from the variable + */ +#define SC_ATOMIC_NAND(name, val) \ + do { \ + SCSpinLock(&(name ## _sc_lock__)); \ + (name ## _sc_atomic__) = ~(name ## _sc_atomic__) & (val); \ + SCSpinUnlock(&(name ## _sc_lock__)); \ + } while(0) + +/** + * \brief Bitwise XOR a value from our atomic variable + * + * \param name the atomic variable + * \param val the value to sub from the variable + */ +#define SC_ATOMIC_XOR(name, val) \ + do { \ + SCSpinLock(&(name ## _sc_lock__)); \ + (name ## _sc_atomic__) ^= (val); \ + SCSpinUnlock(&(name ## _sc_lock__)); \ + } while(0) + /** * \brief Get the value from the atomic variable. * @@ -190,6 +243,47 @@ #define SCAtomicFetchAndSub(addr, value) \ __sync_fetch_and_sub((addr), (value)) +/** + * \brief wrapper for OS/compiler specific atomic fetch and "AND" + * function. + * + * \param addr Address of the variable to AND to + * \param value Value to add to the variable at addr + */ +#define SCAtomicFetchAndAnd(addr, value) \ + __sync_fetch_and_and((addr), (value)) + +/** + * \brief wrapper for OS/compiler specific atomic fetch and "NAND" + * function. + * + * \param addr Address of the variable to NAND to + * \param value Value to add to the variable at addr + */ +#define SCAtomicFetchAndNand(addr, value) \ + __sync_fetch_and_nand((addr), (value)) + +/** + * \brief wrapper for OS/compiler specific atomic fetch and "XOR" + * function. + * + * \param addr Address of the variable to XOR to + * \param value Value to add to the variable at addr + */ +#define SCAtomicFetchAndXor(addr, value) \ + __sync_fetch_and_xor((addr), (value)) + + +/** + * \brief wrapper for OS/compiler specific atomic fetch and or + * function. + * + * \param addr Address of the variable to or to + * \param value Value to add to the variable at addr + */ +#define SCAtomicFetchAndOr(addr, value) \ + __sync_fetch_and_or((addr), (value)) + /** * \brief wrapper for declaring atomic variables. * @@ -264,6 +358,42 @@ #define SC_ATOMIC_SUB(name, val) \ SCAtomicFetchAndSub(&(name ## _sc_atomic__), (val)) +/** + * \brief Bitwise OR a value to our atomic variable + * + * \param name the atomic variable + * \param val the value to OR to the variable + */ +#define SC_ATOMIC_OR(name, val) \ + SCAtomicFetchAndOr(&(name ## _sc_atomic__), (val)) + +/** + * \brief Bitwise AND a value to our atomic variable + * + * \param name the atomic variable + * \param val the value to AND to the variable + */ +#define SC_ATOMIC_AND(name, val) \ + SCAtomicFetchAndAnd(&(name ## _sc_atomic__), (val)) + +/** + * \brief Bitwise NAND a value to our atomic variable + * + * \param name the atomic variable + * \param val the value to NAND to the variable + */ +#define SC_ATOMIC_NAND(name, val) \ + SCAtomicFetchAndNand(&(name ## _sc_atomic__), (val)) + +/** + * \brief Bitwise XOR a value to our atomic variable + * + * \param name the atomic variable + * \param val the value to XOR to the variable + */ +#define SC_ATOMIC_XOR(name, val) \ + SCAtomicFetchAndXor(&(name ## _sc_atomic__), (val)) + /** * \brief atomic Compare and Switch *