From 41195e1ce2eee153823087ceb7ee4dc1bac5bb6f Mon Sep 17 00:00:00 2001 From: Pekka Riikonen Date: Sat, 11 Nov 2006 17:16:54 +0000 Subject: [PATCH] 8, 16 and 32-bit atomic integers, 32 and 64 bit atomic pointers. --- lib/silcutil/silcatomic.h | 1182 ++++++++++++++++++++++++-- lib/silcutil/tests/test_silcatomic.c | 78 +- 2 files changed, 1154 insertions(+), 106 deletions(-) diff --git a/lib/silcutil/silcatomic.h b/lib/silcutil/silcatomic.h index 02a71ca9..b42e77a8 100644 --- a/lib/silcutil/silcatomic.h +++ b/lib/silcutil/silcatomic.h @@ -25,80 +25,205 @@ * simple operations with integers atomically. This enables fast integer * additions and subtractions safely in multithreaded environment. It is * especially suited for reference counters and similar and is much faster - * than using locking. + * than using locking. This interface supports 8, 16 and 32 bit integers + * and 32 or 64 bit pointers. * - * If threads were not enabled when compiling the source code, the operations - * are not atomic. On some platforms this interface actually use mutual - * exclusion lock instead of true atomic operations, leading into some - * performace penalty. + * On some platforms this interface actually use mutual exclusion lock + * instead of true atomic operations, leading into some performace penalty. + * Also on some platforms the 8 and 16 bit integers are actually 32 bit + * integers. + * + * Fast operations are supported on: x86, x86_64, ia64, PPC * ***/ #ifndef SILCATOMIC_H #define SILCATOMIC_H -/****s* silcutil/SilcAtomicAPI/SilcAtomic +/****s* silcutil/SilcAtomicAPI/SilcAtomic32 + * + * NAME + * + * typedef struct { ... } SilcAtomic32; + * + * DESCRIPTION + * + * The atomic operation structure given as argument to all atomic + * operation functions. It hols the actual 32-bit atomic variable. + * + * EXAMPLE + * + * SilcAtomic32 refcnt; + * + * // Initialize atomic variable + * silc_atomic_init32(&refcnt, 0); + * + * ... + * // Increment referene counter + * silc_atomic_add_int32(&refcnt, 1); + * ... + * + * // Uninitialize atomic variable + * silc_atomic_uninit32(&refcnt); + * + ***/ + +/****s* silcutil/SilcAtomicAPI/SilcAtomic16 + * + * NAME + * + * typedef struct { ... } SilcAtomic16; + * + * DESCRIPTION + * + * The atomic operation structure given as argument to all atomic + * operation functions. It hols the actual 16-bit atomic variable. + * + * EXAMPLE + * + * SilcAtomic16 refcnt; + * + * // Initialize atomic variable + * silc_atomic_init16(&refcnt, 0); + * + * ... + * // Increment referene counter + * silc_atomic_add_int16(&refcnt, 1); + * ... + * + * // Uninitialize atomic variable + * silc_atomic_uninit16(&refcnt); + * + ***/ + +/****s* silcutil/SilcAtomicAPI/SilcAtomic8 * * NAME * - * typedef struct { ... } SilcAtomic; + * typedef struct { ... } SilcAtomic8; * * DESCRIPTION * * The atomic operation structure given as argument to all atomic - * operation functions. It hols the actual atomic variable. On most - * platforms its size is 32 bits but on some platforms it may be - * larger. + * operation functions. It hols the actual 8-bit atomic variable. * * EXAMPLE * - * SilcAtomic refcnt; + * SilcAtomic8 refcnt; * * // Initialize atomic variable - * silc_atomic_init(&refcnt, 0); + * silc_atomic_init8(&refcnt, 0); * * ... * // Increment referene counter - * silc_atomic_add_int(&refcnt, 1); + * silc_atomic_add_int8(&refcnt, 1); + * ... + * + * // Uninitialize atomic variable + * silc_atomic_uninit8(&refcnt); + * + ***/ + +/****s* silcutil/SilcAtomicAPI/SilcAtomicPointer + * + * NAME + * + * typedef struct { ... } SilcAtomicPointer; + * + * DESCRIPTION + * + * The atomic operation structure given as argument to all atomic + * operation functions. It hols the actual pointer variable. + * + * EXAMPLE + * + * SilcAtomicPointer ptr; + * + * // Initialize atomic variable + * silc_atomic_init_pointer(&ptr, NULL); + * + * ... + * // Set pointer + * silc_atomic_set_pointer(&ptr, context); * ... * * // Uninitialize atomic variable - * silc_atomic_uninit(&refcnt); + * silc_atomic_uninit_pointer(&ptr); * ***/ + #if !defined(SILC_THREADS) || defined(SILC_WIN32) || (defined(__GNUC__) && \ (defined(SILC_I486) || defined(SILC_X86_64) || defined(SILC_IA64) || \ defined(SILC_POWERPC))) -typedef struct -{ +typedef struct { volatile SilcUInt32 value; -} SilcAtomic; +} SilcAtomic32; +typedef struct { + volatile void *pointer; +} SilcAtomicPointer; #else #define SILC_ATOMIC_MUTEX -typedef struct -{ +typedef struct { + SilcMutex lock; + volatile SilcUInt32 value; +} SilcAtomic32; +typedef struct { + SilcMutex lock; + volatile void *pointer; +} SilcAtomicPointer; +#endif + +#if !defined(SILC_THREADS) || (defined(__GNUC__) && (defined(SILC_I486) || \ + defined(SILC_X86_64))) +typedef struct { + volatile SilcUInt16 value; +} SilcAtomic16; +#elif defined(SILC_WIN32) || (defined(__GNUC__) && (defined(SILC_IA64) || \ + defined(SILC_POWERPC))) +typedef struct { + volatile SilcUInt32 value; +} SilcAtomic16; +#else +typedef struct { + SilcMutex lock; + volatile SilcUInt16 value; +} SilcAtomic16; +#endif + +#if !defined(SILC_THREADS) || (defined(__GNUC__) && (defined(SILC_I486) || \ + defined(SILC_X86_64))) +typedef struct { + volatile SilcUInt8 value; +} SilcAtomic8; +#elif defined(SILC_WIN32) || (defined(__GNUC__) && (defined(SILC_IA64) || \ + defined(SILC_POWERPC))) +typedef struct { volatile SilcUInt32 value; +} SilcAtomic8; +#else +typedef struct { SilcMutex lock; -} SilcAtomic; + volatile SilcUInt8 value; +} SilcAtomic8; #endif -/****f* silcutil/SilcAtomicAPI/silc_atomic_init +/****f* silcutil/SilcAtomicAPI/silc_atomic_init32 * * SYNOPSIS * * static inline - * SilcBool silc_atomic_init(SilcAtomic *atomic, int value); + * SilcBool silc_atomic_init32(SilcAtomic32 *atomic, SilcUInt32 value); * * DESCRIPTION * * Initializes the atomic variable `atomic', and sets the `value' as its * inital value. Returns FALSE on error. To uninitialize call the - * silc_atomic_uninit function. + * silc_atomic_uninit32 function. * ***/ static inline -SilcBool silc_atomic_init(SilcAtomic *atomic, int value) +SilcBool silc_atomic_init32(SilcAtomic32 *atomic, SilcUInt32 value) { atomic->value = value; @@ -110,152 +235,1029 @@ SilcBool silc_atomic_init(SilcAtomic *atomic, int value) return TRUE; } -/****f* silcutil/SilcAtomicAPI/silc_atomic_uninit +/****f* silcutil/SilcAtomicAPI/silc_atomic_init16 * * SYNOPSIS * * static inline - * void silc_atomic_uninit(SilcAtomic *atomic); + * SilcBool silc_atomic_init16(SilcAtomic16 *atomic, SilcUInt16 value); * * DESCRIPTION * - * Uninitializes the atomic variable `atomic'. This should alwyas be - * called after the atomic variable is not used anymore. + * Initializes the atomic variable `atomic', and sets the `value' as its + * inital value. Returns FALSE on error. To uninitialize call the + * silc_atomic_uninit32 function. * ***/ static inline -void silc_atomic_uninit(SilcAtomic *atomic) +SilcBool silc_atomic_init16(SilcAtomic16 *atomic, SilcUInt16 value) { - atomic->value = 0; + atomic->value = value; + #if defined(SILC_ATOMIC_MUTEX) - silc_mutex_free(atomic->lock); + if (!silc_mutex_alloc(&atomic->lock)) + return FALSE; #endif /* SILC_ATOMIC_MUTEX */ + + return TRUE; } -/****f* silcutil/SilcAtomicAPI/silc_atomic_add_int +/****f* silcutil/SilcAtomicAPI/silc_atomic_init8 * * SYNOPSIS * * static inline - * SilcUInt32 silc_atomic_add_int(SilcAtomic *atomic, int value); + * SilcBool silc_atomic_init8(SilcAtomic8 *atomic, SilcUInt8 value); * * DESCRIPTION * - * Atomically adds `value' to 32-bit integer. Returns the value after - * addition. + * Initializes the atomic variable `atomic', and sets the `value' as its + * inital value. Returns FALSE on error. To uninitialize call the + * silc_atomic_uninit8 function. * ***/ static inline -SilcUInt32 silc_atomic_add_int(SilcAtomic *atomic, int value) +SilcBool silc_atomic_init8(SilcAtomic8 *atomic, SilcUInt8 value) { - SilcUInt32 ret; + atomic->value = value; -#if !defined(SILC_THREADS) - /* No atomic operations */ - ret = atomic->value; - atomic->value += value; +#if defined(SILC_ATOMIC_MUTEX) + if (!silc_mutex_alloc(&atomic->lock)) + return FALSE; +#endif /* SILC_ATOMIC_MUTEX */ -#elif defined(SILC_WIN32) - /* Windows */ - ret = InterlockedExchangeAdd(&atomic->value, (LONG)value); + return TRUE; +} -#elif defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64)) - /* GCC + i486 or x86_64 */ - __asm __volatile("lock; xaddl %0, %1" - : "=r" (ret), "+m" (atomic->value) - : "0" (value)); +/****f* silcutil/SilcAtomicAPI/silc_atomic_init_pointer + * + * SYNOPSIS + * + * static inline + * SilcBool silc_atomic_init_pointer(SilcAtomicPointer *atomic, + * void *pointer); + * + * DESCRIPTION + * + * Initializes the atomic pointer variable `atomic', and sets the `pointer' + * as its inital pointer. Returns FALSE on error. To uninitialize call + * the silc_atomic_uninit_pointer function. + * + ***/ -#elif defined(__GNUC__) && defined(SILC_IA64) - /* GCC + IA64 (GCC builtin atomic operations) */ - ret = __sync_fetch_and_add(&atomic->value, value); +static inline +SilcBool silc_atomic_init_pointer(SilcAtomicPointer *atomic, void *pointer) +{ + atomic->pointer = pointer; -#elif defined(__GNUC__) && defined(SILC_POWERPC) - /* GCC + PowerPC (code adapted from IBM's documentation) */ - /* XXX Hmm.. should I sync and isync this?... */ - __asm __volatile("0: lwarx %0, 0, %2\n" - " add %0, %1, %0\n" - " stwcx. %0, 0, %2\n" - " bne- 0b" - : "=&r" (ret) - : "r" (value), "r" (&atomic->value) - : "cc"); - return ret; +#if defined(SILC_ATOMIC_MUTEX) + if (!silc_mutex_alloc(&atomic->lock)) + return FALSE; +#endif /* SILC_ATOMIC_MUTEX */ -#else - /* Mutex */ - silc_mutex_lock(atomic->lock); - ret = atomic->value; - atomic->value += value; - silc_mutex_unlock(atomic->lock); -#endif + return TRUE; +} - return ret + value; +/****f* silcutil/SilcAtomicAPI/silc_atomic_uninit32 + * + * SYNOPSIS + * + * static inline + * void silc_atomic_uninit32(SilcAtomic32 *atomic); + * + * DESCRIPTION + * + * Uninitializes the atomic variable `atomic'. This should alwyas be + * called after the atomic variable is not used anymore. + * + ***/ + +static inline +void silc_atomic_uninit32(SilcAtomic32 *atomic) +{ + atomic->value = 0; +#if defined(SILC_ATOMIC_MUTEX) + silc_mutex_free(atomic->lock); +#endif /* SILC_ATOMIC_MUTEX */ } -/****f* silcutil/SilcAtomicAPI/silc_atomic_sub_int +/****f* silcutil/SilcAtomicAPI/silc_atomic_uninit16 * * SYNOPSIS * * static inline - * SilcUInt32 silc_atomic_sub_int(SilcAtomic *atomic, int value); + * void silc_atomic_uninit16(SilcAtomic16 *atomic); * * DESCRIPTION * - * Atomically subtracts `value' from 32-bit integer. Returns the value - * after subtraction. + * Uninitializes the atomic variable `atomic'. This should alwyas be + * called after the atomic variable is not used anymore. * ***/ static inline -SilcUInt32 silc_atomic_sub_int(SilcAtomic *atomic, int value) +void silc_atomic_uninit16(SilcAtomic16 *atomic) { - return silc_atomic_add_int(atomic, -value); + atomic->value = 0; +#if defined(SILC_ATOMIC_MUTEX) + silc_mutex_free(atomic->lock); +#endif /* SILC_ATOMIC_MUTEX */ } -/****f* silcutil/SilcAtomicAPI/silc_atomic_get_int +/****f* silcutil/SilcAtomicAPI/silc_atomic_uninit8 * * SYNOPSIS * * static inline - * SilcUInt32 silc_atomic_get_int(SilcAtomic *atomic); + * void silc_atomic_uninit8(SilcAtomic8 *atomic); * * DESCRIPTION * - * Returns the current value of the atomic variable. + * Uninitializes the atomic variable `atomic'. This should alwyas be + * called after the atomic variable is not used anymore. * ***/ static inline -SilcUInt32 silc_atomic_get_int(SilcAtomic *atomic) +void silc_atomic_uninit8(SilcAtomic8 *atomic) { - SilcUInt32 ret; + atomic->value = 0; +#if defined(SILC_ATOMIC_MUTEX) + silc_mutex_free(atomic->lock); +#endif /* SILC_ATOMIC_MUTEX */ +} + +/****f* silcutil/SilcAtomicAPI/silc_atomic_uninit_pointer + * + * SYNOPSIS + * + * static inline + * void silc_atomic_uninit_pointer(SilcAtomicPointer *atomic); + * + * DESCRIPTION + * + * Uninitializes the atomic variable `atomic'. This should alwyas be + * called after the atomic variable is not used anymore. + * + ***/ + +static inline +void silc_atomic_uninit_pointer(SilcAtomicPointer *atomic) +{ + atomic->pointer = NULL; +#if defined(SILC_ATOMIC_MUTEX) + silc_mutex_free(atomic->lock); +#endif /* SILC_ATOMIC_MUTEX */ +} + +/****f* silcutil/SilcAtomicAPI/silc_atomic_set_int32 + * + * SYNOPSIS + * + * static inline + * void silc_atomic_set_int32(SilcAtomic32 *atomic, SilcUInt32 value); + * + * DESCRIPTION + * + * Atomically sets `value' to 32-bit integer. + * + ***/ +static inline +void silc_atomic_set_int32(SilcAtomic32 *atomic, SilcUInt32 value) +{ #if !defined(SILC_THREADS) || defined(SILC_WIN32) || \ (defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64))) /* No threads, Windows, i486 or x86_64, no memory barrier needed */ - ret = atomic->value; - return ret; + atomic->value = value; #elif defined(__GNUC__) && defined(SILC_IA64) /* IA64, memory barrier needed */ + atomic->value = value; __sync_synchronize(); - ret = atomic->value; - return ret; #elif defined(__GNUC__) && defined(SILC_POWERPC) /* PowerPC, memory barrier needed */ + atomic->value = value; __asm("sync" : : : "memory"); - ret = atomic->value; - return ret; #else /* Mutex */ silc_mutex_lock(atomic->lock); - ret = atomic->value; + atomic->value = value; silc_mutex_unlock(atomic->lock); - return ret; +#endif +} + +/****f* silcutil/SilcAtomicAPI/silc_atomic_set_int16 + * + * SYNOPSIS + * + * static inline + * void silc_atomic_set_int16(SilcAtomic16 *atomic, SilcUInt16 value); + * + * DESCRIPTION + * + * Atomically sets `value' to 16-bit integer. + * + ***/ + +static inline +void silc_atomic_set_int16(SilcAtomic16 *atomic, SilcUInt16 value) +{ +#if !defined(SILC_THREADS) || defined(SILC_WIN32) || \ + (defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64))) + /* No threads, Windows, i486 or x86_64, no memory barrier needed */ + atomic->value = value; + +#elif defined(__GNUC__) && defined(SILC_IA64) + /* IA64, memory barrier needed */ + atomic->value = value; + __sync_synchronize(); + +#elif defined(__GNUC__) && defined(SILC_POWERPC) + /* PowerPC, memory barrier needed */ + atomic->value = value; + __asm("sync" : : : "memory"); + +#else + /* Mutex */ + silc_mutex_lock(atomic->lock); + atomic->value = value; + silc_mutex_unlock(atomic->lock); +#endif +} + +/****f* silcutil/SilcAtomicAPI/silc_atomic_set_int8 + * + * SYNOPSIS + * + * static inline + * void silc_atomic_set_int8(SilcAtomic8 *atomic, SilcUInt8 value); + * + * DESCRIPTION + * + * Atomically sets `value' to 8-bit integer. + * + ***/ + +static inline +void silc_atomic_set_int8(SilcAtomic8 *atomic, SilcUInt8 value) +{ +#if !defined(SILC_THREADS) || defined(SILC_WIN32) || \ + (defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64))) + /* No threads, Windows, i486 or x86_64, no memory barrier needed */ + atomic->value = value; + +#elif defined(__GNUC__) && defined(SILC_IA64) + /* IA64, memory barrier needed */ + atomic->value = value; + __sync_synchronize(); + +#elif defined(__GNUC__) && defined(SILC_POWERPC) + /* PowerPC, memory barrier needed */ + atomic->value = value; + __asm("sync" : : : "memory"); + +#else + /* Mutex */ + silc_mutex_lock(atomic->lock); + atomic->value = value; + silc_mutex_unlock(atomic->lock); +#endif +} + +/****f* silcutil/SilcAtomicAPI/silc_atomic_set_pointer + * + * SYNOPSIS + * + * static inline + * void silc_atomic_set_pointer(SilcAtomicPointer *atomic, void *pointer); + * + * DESCRIPTION + * + * Atomically sets `pointer' to the atomic variable. + * + ***/ + +static inline +void silc_atomic_set_pointer(SilcAtomicPointer *atomic, void *pointer) +{ +#if !defined(SILC_THREADS) || defined(SILC_WIN32) || \ + (defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64))) + /* No threads, Windows, i486 or x86_64, no memory barrier needed */ + atomic->pointer = pointer; + +#elif defined(__GNUC__) && defined(SILC_IA64) + /* IA64, memory barrier needed */ + atomic->pointer = pointer; + __sync_synchronize(); + +#elif defined(__GNUC__) && defined(SILC_POWERPC) + /* PowerPC, memory barrier needed */ + atomic->pointer = pointer; + __asm("sync" : : : "memory"); + +#else + /* Mutex */ + silc_mutex_lock(atomic->lock); + atomic->pointer = pointer; + silc_mutex_unlock(atomic->lock); +#endif +} + +/****f* silcutil/SilcAtomicAPI/silc_atomic_get_int32 + * + * SYNOPSIS + * + * static inline + * SilcUInt32 silc_atomic_get_int32(SilcAtomic32 *atomic); + * + * DESCRIPTION + * + * Returns the current value of the atomic variable. + * + ***/ + +static inline +SilcUInt32 silc_atomic_get_int32(SilcAtomic32 *atomic) +{ + SilcUInt32 ret; + +#if !defined(SILC_THREADS) || defined(SILC_WIN32) || \ + (defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64))) + /* No threads, Windows, i486 or x86_64, no memory barrier needed */ + ret = atomic->value; + return ret; + +#elif defined(__GNUC__) && defined(SILC_IA64) + /* IA64, memory barrier needed */ + __sync_synchronize(); + ret = atomic->value; + return ret; + +#elif defined(__GNUC__) && defined(SILC_POWERPC) + /* PowerPC, memory barrier needed */ + __asm("sync" : : : "memory"); + ret = atomic->value; + return ret; + +#else + /* Mutex */ + silc_mutex_lock(atomic->lock); + ret = atomic->value; + silc_mutex_unlock(atomic->lock); + return ret; +#endif +} + +/****f* silcutil/SilcAtomicAPI/silc_atomic_get_int16 + * + * SYNOPSIS + * + * static inline + * SilcUInt32 silc_atomic_get_int16(SilcAtomic16 *atomic); + * + * DESCRIPTION + * + * Returns the current value of the atomic variable. + * + ***/ + +static inline +SilcUInt16 silc_atomic_get_int16(SilcAtomic16 *atomic) +{ + SilcUInt16 ret; + +#if !defined(SILC_THREADS) || defined(SILC_WIN32) || \ + (defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64))) + /* No threads, Windows, i486 or x86_64, no memory barrier needed */ + ret = atomic->value & 0xffff; + return ret; + +#elif defined(__GNUC__) && defined(SILC_IA64) + /* IA64, memory barrier needed */ + __sync_synchronize(); + ret = atomic->value & 0xffff; + return ret; + +#elif defined(__GNUC__) && defined(SILC_POWERPC) + /* PowerPC, memory barrier needed */ + __asm("sync" : : : "memory"); + ret = atomic->value & 0xffff; + return ret; + +#else + /* Mutex */ + silc_mutex_lock(atomic->lock); + ret = atomic->value & 0xffff; + silc_mutex_unlock(atomic->lock); + return ret; +#endif +} + +/****f* silcutil/SilcAtomicAPI/silc_atomic_get_int8 + * + * SYNOPSIS + * + * static inline + * SilcUInt32 silc_atomic_get_int8(SilcAtomic8 *atomic); + * + * DESCRIPTION + * + * Returns the current value of the atomic variable. + * + ***/ + +static inline +SilcUInt8 silc_atomic_get_int8(SilcAtomic8 *atomic) +{ + SilcUInt8 ret; + +#if !defined(SILC_THREADS) || defined(SILC_WIN32) || \ + (defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64))) + /* No threads, Windows, i486 or x86_64, no memory barrier needed */ + ret = atomic->value & 0xff; + return ret; + +#elif defined(__GNUC__) && defined(SILC_IA64) + /* IA64, memory barrier needed */ + __sync_synchronize(); + ret = atomic->value & 0xff; + return ret; + +#elif defined(__GNUC__) && defined(SILC_POWERPC) + /* PowerPC, memory barrier needed */ + __asm("sync" : : : "memory"); + ret = atomic->value & 0xff; + return ret; + +#else + /* Mutex */ + silc_mutex_lock(atomic->lock); + ret = atomic->value & 0xff; + silc_mutex_unlock(atomic->lock); + return ret; +#endif +} + +/****f* silcutil/SilcAtomicAPI/silc_atomic_get_pointer + * + * SYNOPSIS + * + * static inline + * SilcUInt8 silc_atomic_get_pointer(SilcAtomicPointer *atomic) + * + * DESCRIPTION + * + * Returns the current pointer value of the atomic variable. + * + ***/ + +static inline +void *silc_atomic_get_pointer(SilcAtomicPointer *atomic) +{ + void *ret; + +#if !defined(SILC_THREADS) || defined(SILC_WIN32) || \ + (defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64))) + /* No threads, Windows, i486 or x86_64, no memory barrier needed */ + ret = (void *)atomic->pointer; + return ret; + +#elif defined(__GNUC__) && defined(SILC_IA64) + /* IA64, memory barrier needed */ + __sync_synchronize(); + ret = (void *)atomic->pointer; + return ret; + +#elif defined(__GNUC__) && defined(SILC_POWERPC) + /* PowerPC, memory barrier needed */ + __asm("sync" : : : "memory"); + ret = (void *)atomic->pointer; + return ret; + +#else + /* Mutex */ + silc_mutex_lock(atomic->lock); + ret = (void *)atomic->pointer; + silc_mutex_unlock(atomic->lock); + return ret; +#endif +} + +/****f* silcutil/SilcAtomicAPI/silc_atomic_add_int32 + * + * SYNOPSIS + * + * static inline + * SilcUInt32 silc_atomic_add_int32(SilcAtomic32 *atomic, SilcInt32 value); + * + * DESCRIPTION + * + * Atomically adds `value' to 32-bit integer. Returns the value after + * addition. + * + ***/ + +static inline +SilcUInt32 silc_atomic_add_int32(SilcAtomic32 *atomic, SilcInt32 value) +{ + SilcUInt32 ret; + +#if !defined(SILC_THREADS) + /* No atomic operations */ + ret = atomic->value; + atomic->value += value; + +#elif defined(SILC_WIN32) + /* Windows */ + ret = InterlockedExchangeAdd(&atomic->value, (LONG)value); + +#elif defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64)) + /* GCC + i486 or x86_64 */ + __asm __volatile("lock; xaddl %0, %1" + : "=r" (ret), "+m" (atomic->value) + : "0" (value)); + +#elif defined(__GNUC__) && defined(SILC_IA64) + /* GCC + IA64 (GCC builtin atomic operations) */ + ret = __sync_fetch_and_add(&atomic->value, value); + +#elif defined(__GNUC__) && defined(SILC_POWERPC) + /* GCC + PowerPC (code adapted from IBM's documentation) */ + __asm __volatile("0: lwarx %0, 0, %2\n" + " add %0, %1, %0\n" + " stwcx. %0, 0, %2\n" + " bne- 0b" + : "=&r" (ret) + : "r" (value), "r" (&atomic->value) + : "cc"); + return ret; + +#else + /* Mutex */ + silc_mutex_lock(atomic->lock); + ret = atomic->value; + atomic->value += value; + silc_mutex_unlock(atomic->lock); +#endif + + return ret + value; +} + +/****f* silcutil/SilcAtomicAPI/silc_atomic_add_int32 + * + * SYNOPSIS + * + * static inline + * SilcUInt16 silc_atomic_add_int16(SilcAtomic16 *atomic, SilcInt16 value); + * + * DESCRIPTION + * + * Atomically adds `value' to 16-bit integer. Returns the value after + * addition. + * + ***/ + +static inline +SilcUInt16 silc_atomic_add_int16(SilcAtomic16 *atomic, SilcInt16 value) +{ + SilcUInt16 ret; + +#if !defined(SILC_THREADS) + /* No atomic operations */ + ret = atomic->value; + atomic->value += value; + +#elif defined(SILC_WIN32) + /* Windows */ + LONG v = value; + ret = InterlockedExchangeAdd(&atomic->value, v); + +#elif defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64)) + /* GCC + i486 or x86_64 */ + __asm __volatile("lock; xaddw %0, %1" + : "=c" (ret), "+m" (atomic->value) + : "0" (value)); + +#elif defined(__GNUC__) && defined(SILC_IA64) + /* GCC + IA64 (GCC builtin atomic operations) */ + SilcInt32 v = value; + ret = __sync_fetch_and_add(&atomic->value, v); + +#elif defined(__GNUC__) && defined(SILC_POWERPC) + /* GCC + PowerPC (code adapted from IBM's documentation) */ + SilcUInt32 ret32; + SilcInt32 v = value; + __asm __volatile("0: lwarx %0, 0, %2\n" + " add %0, %1, %0\n" + " stwcx. %0, 0, %2\n" + " bne- 0b" + : "=&r" (ret32) + : "r" (v), "r" (&atomic->value) + : "cc"); + return ret32 & 0xffff; + +#else + /* Mutex */ + silc_mutex_lock(atomic->lock); + ret = atomic->value; + atomic->value += value; + silc_mutex_unlock(atomic->lock); +#endif + + return ret + value; +} + +/****f* silcutil/SilcAtomicAPI/silc_atomic_add_int8 + * + * SYNOPSIS + * + * static inline + * SilcUInt8 silc_atomic_add_int8(SilcAtomic8 *atomic, SilcInt8 value); + * + * DESCRIPTION + * + * Atomically adds `value' to 8-bit integer. Returns the value after + * addition. + * + ***/ + +static inline +SilcUInt8 silc_atomic_add_int8(SilcAtomic8 *atomic, SilcInt8 value) +{ + SilcUInt8 ret; + +#if !defined(SILC_THREADS) + /* No atomic operations */ + ret = atomic->value; + atomic->value += value; + +#elif defined(SILC_WIN32) + /* Windows */ + LONG v = value; + ret = InterlockedExchangeAdd(&atomic->value, v); + +#elif defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64)) + /* GCC + i486 or x86_64 */ + __asm __volatile("lock; xaddb %0, %1" + : "=c" (ret), "+m" (atomic->value) + : "0" (value)); + +#elif defined(__GNUC__) && defined(SILC_IA64) + /* GCC + IA64 (GCC builtin atomic operations) */ + SilcInt32 v = value; + ret = __sync_fetch_and_add(&atomic->value, v); + +#elif defined(__GNUC__) && defined(SILC_POWERPC) + /* GCC + PowerPC (code adapted from IBM's documentation) */ + SilcUInt32 ret32; + SilcInt32 v = value; + __asm __volatile("0: lwarx %0, 0, %2\n" + " add %0, %1, %0\n" + " stwcx. %0, 0, %2\n" + " bne- 0b" + : "=&r" (ret32) + : "r" (v), "r" (&atomic->value) + : "cc"); + return ret32 & 0xff; + +#else + /* Mutex */ + silc_mutex_lock(atomic->lock); + ret = atomic->value; + atomic->value += value; + silc_mutex_unlock(atomic->lock); +#endif + + return ret + value; +} + +/****f* silcutil/SilcAtomicAPI/silc_atomic_sub_int32 + * + * SYNOPSIS + * + * static inline + * SilcUInt32 silc_atomic_sub_int32(SilcAtomic32 *atomic, SilcInt32 value); + * + * DESCRIPTION + * + * Atomically subtracts `value' from 32-bit integer. Returns the value + * after subtraction. + * + ***/ + +static inline +SilcUInt32 silc_atomic_sub_int32(SilcAtomic32 *atomic, SilcInt32 value) +{ + return silc_atomic_add_int32(atomic, -value); +} + +/****f* silcutil/SilcAtomicAPI/silc_atomic_sub_int16 + * + * SYNOPSIS + * + * static inline + * SilcUInt16 silc_atomic_sub_int16(SilcAtomic16 *atomic, SilcInt16 value); + * + * DESCRIPTION + * + * Atomically subtracts `value' from 16-bit integer. Returns the value + * after subtraction. + * + ***/ + +static inline +SilcUInt16 silc_atomic_sub_int16(SilcAtomic16 *atomic, SilcInt16 value) +{ + return silc_atomic_add_int16(atomic, -value); +} + +/****f* silcutil/SilcAtomicAPI/silc_atomic_sub_int8 + * + * SYNOPSIS + * + * static inline + * SilcUInt8 silc_atomic_sub_int8(SilcAtomic8 *atomic, SilcInt8 value); + * + * DESCRIPTION + * + * Atomically subtracts `value' from 8-bit integer. Returns the value + * after subtraction. + * + ***/ + +static inline +SilcUInt8 silc_atomic_sub_int8(SilcAtomic8 *atomic, SilcInt8 value) +{ + return silc_atomic_add_int8(atomic, -value); +} + +/****f* silcutil/SilcAtomicAPI/silc_atomic_cas32 + * + * SYNOPSIS + * + * static inline + * SilcBool silc_atomic_cas32(SilcAtomic32 *atomic, SilcUInt32 old_val, + * SilcUInt32 new_val) + * + * DESCRIPTION + * + * Performs compare and swap (CAS). Atomically compares if the variable + * `atomic' has the value `old_val' and in that case swaps it with the + * value `new_val'. Returns TRUE if the old value was same and it was + * swapped and FALSE if it differed and was not swapped. + * + ***/ + +static inline +SilcBool silc_atomic_cas32(SilcAtomic32 *atomic, SilcUInt32 old_val, + SilcUInt32 new_val) +{ + SilcUInt32 ret; + +#if !defined(SILC_THREADS) + /* No atomic operations */ + if (atomic->value == old_val) { + atomic->value = new_val; + return TRUE; + } + return FALSE; + +#elif defined(SILC_WIN32) + /* Windows */ + return InterlockedCompareExchange(&atomic->value, (LONG)new_val, + (LONG)old_val) == old_val; + +#elif defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64)) + /* GCC + i486 or x86_64 */ + __asm __volatile("lock; cmpxchgl %2, %1" + : "=a" (ret), "=m" (atomic->value) + : "r" (new_val), "m" (atomic->value), "0" (old_val)); + return ret == old_val; + +#elif defined(__GNUC__) && defined(SILC_IA64) + /* GCC + IA64 (GCC builtin atomic operations) */ + return __sync_bool_compare_and_swap(&atomic->value, old_val, new_val); + +#elif defined(__GNUC__) && defined(SILC_POWERPC) + /* GCC + PowerPC */ + /* XXX TODO */ + +#else + /* Mutex */ + silc_mutex_lock(atomic->lock); + if (atomic->value == old_val) { + atomic->value = new_val; + silc_mutex_unlock(atomic->lock); + return TRUE; + } + silc_mutex_unlock(atomic->lock); + return FALSE; +#endif +} + +/****f* silcutil/SilcAtomicAPI/silc_atomic_cas16 + * + * SYNOPSIS + * + * static inline + * SilcBool silc_atomic_cas16(SilcAtomic16 *atomic, SilcUInt16 old_val, + * SilcUInt16 new_val) + * + * DESCRIPTION + * + * Performs compare and swap (CAS). Atomically compares if the variable + * `atomic' has the value `old_val' and in that case swaps it with the + * value `new_val'. Returns TRUE if the old value was same and it was + * swapped and FALSE if it differed and was not swapped. + * + ***/ + +static inline +SilcBool silc_atomic_cas16(SilcAtomic16 *atomic, SilcUInt16 old_val, + SilcUInt16 new_val) +{ + SilcUInt16 ret; + +#if !defined(SILC_THREADS) + /* No atomic operations */ + if (atomic->value == old_val) { + atomic->value = new_val; + return TRUE; + } + return FALSE; + +#elif defined(SILC_WIN32) + /* Windows */ + LONG o = old_val, n = new_val; + return InterlockedCompareExchange(&atomic->value, n, o) == o; + +#elif defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64)) + /* GCC + i486 or x86_64 */ + __asm __volatile("lock; cmpxchgw %2, %1" + : "=a" (ret), "=m" (atomic->value) + : "c" (new_val), "m" (atomic->value), "0" (old_val)); + return ret == old_val; + +#elif defined(__GNUC__) && defined(SILC_IA64) + /* GCC + IA64 (GCC builtin atomic operations) */ + SilcUInt32 o = old_val, n = new_val; + return __sync_bool_compare_and_swap(&atomic->value, o, n); + +#elif defined(__GNUC__) && defined(SILC_POWERPC) + /* GCC + PowerPC */ + /* XXX TODO */ + +#else + /* Mutex */ + silc_mutex_lock(atomic->lock); + if (atomic->value == old_val) { + atomic->value = new_val; + silc_mutex_unlock(atomic->lock); + return TRUE; + } + silc_mutex_unlock(atomic->lock); + return FALSE; +#endif +} + +/****f* silcutil/SilcAtomicAPI/silc_atomic_cas8 + * + * SYNOPSIS + * + * static inline + * SilcBool silc_atomic_cas8(SilcAtomic8 *atomic, SilcUInt8 old_val, + * SilcUInt8 new_val) + * + * DESCRIPTION + * + * Performs compare and swap (CAS). Atomically compares if the variable + * `atomic' has the value `old_val' and in that case swaps it with the + * value `new_val'. Returns TRUE if the old value was same and it was + * swapped and FALSE if it differed and was not swapped. + * + ***/ + +static inline +SilcBool silc_atomic_cas8(SilcAtomic8 *atomic, SilcUInt8 old_val, + SilcUInt8 new_val) +{ + SilcUInt8 ret; + +#if !defined(SILC_THREADS) + /* No atomic operations */ + if (atomic->value == old_val) { + atomic->value = new_val; + return TRUE; + } + return FALSE; + +#elif defined(SILC_WIN32) + /* Windows */ + LONG o = old_val, n = new_val; + return InterlockedCompareExchange(&atomic->value, n, o) == o; + +#elif defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64)) + /* GCC + i486 or x86_64 */ + __asm __volatile("lock; cmpxchgb %2, %1" + : "=a" (ret), "=m" (atomic->value) + : "c" (new_val), "m" (atomic->value), "0" (old_val)); + return ret == old_val; + +#elif defined(__GNUC__) && defined(SILC_IA64) + /* GCC + IA64 (GCC builtin atomic operations) */ + SilcUInt32 o = old_val, n = new_val; + return __sync_bool_compare_and_swap(&atomic->value, o, n); + +#elif defined(__GNUC__) && defined(SILC_POWERPC) + /* GCC + PowerPC */ + /* XXX TODO */ + +#else + /* Mutex */ + silc_mutex_lock(atomic->lock); + if (atomic->value == old_val) { + atomic->value = new_val; + silc_mutex_unlock(atomic->lock); + return TRUE; + } + silc_mutex_unlock(atomic->lock); + return FALSE; +#endif +} + +/****f* silcutil/SilcAtomicAPI/silc_atomic_cas_pointer + * + * SYNOPSIS + * + * static inline + * SilcBool silc_atomic_cas_pointer(SilcAtomicPointer *atomic, + * void *old_ptr, void *new_ptr); + * + * DESCRIPTION + * + * Performs compare and swap (CAS). Atomically compares if the variable + * `atomic' has the pointer `old_ptr' and in that case swaps it with the + * pointer `new_ptr'. Returns TRUE if the old pointer was same and it was + * swapped and FALSE if it differed and was not swapped. + * + ***/ + +static inline +SilcBool silc_atomic_cas_pointer(SilcAtomicPointer *atomic, void *old_val, + void *new_val) +{ + void *ret; + +#if !defined(SILC_THREADS) + /* No atomic operations */ + if (atomic->pointer == old_val) { + atomic->pointer = new_val; + return TRUE; + } + return FALSE; + +#elif defined(SILC_WIN32) + /* Windows */ + return InterlockedCompareExchangePointer(&atomic->pointer, n, o) == o; + +#elif defined(__GNUC__) && defined(SILC_I486) + /* GCC + i486 */ + __asm __volatile("lock; cmpxchgl %2, %1" + : "=a" (ret), "=m" (atomic->pointer) + : "c" (new_val), "m" (atomic->pointer), "0" (old_val)); + return ret == old_val; + +#elif defined(__GNUC__) && defined(SILC_X86_64) + /* GCC + x86_64 */ + __asm __volatile("lock; cmpxchgq %q2, %1" + : "=a" (ret), "=m" (atomic->pointer) + : "c" (new_val), "m" (atomic->pointer), "0" (old_val)); + return ret == old_val; + +#elif defined(__GNUC__) && defined(SILC_IA64) + /* GCC + IA64 (GCC builtin atomic operations) */ + return __sync_bool_compare_and_swap((long)&atomic->pointer, (long)old_val, + (long)new_val); + +#elif defined(__GNUC__) && defined(SILC_POWERPC) + /* GCC + PowerPC */ + /* XXX TODO */ + +#else + /* Mutex */ + silc_mutex_lock(atomic->lock); + if (atomic->pointer == old_val) { + atomic->pointer = new_val; + silc_mutex_unlock(atomic->lock); + return TRUE; + } + silc_mutex_unlock(atomic->lock); + return FALSE; #endif } diff --git a/lib/silcutil/tests/test_silcatomic.c b/lib/silcutil/tests/test_silcatomic.c index feaf2895..89405a4d 100644 --- a/lib/silcutil/tests/test_silcatomic.c +++ b/lib/silcutil/tests/test_silcatomic.c @@ -6,7 +6,10 @@ int main(int argc, char **argv) { SilcBool success = FALSE; - SilcAtomic ref; + SilcAtomic32 ref32; + SilcAtomic16 ref16; + SilcAtomic8 ref8; + SilcAtomicPointer refptr; SilcUInt8 ret8; SilcUInt16 ret16; SilcUInt32 ret32; @@ -17,34 +20,77 @@ int main(int argc, char **argv) silc_log_set_debug_string("*atomic*"); } - silc_atomic_init(&ref, 1); + silc_atomic_init8(&ref8, 1); + silc_atomic_init16(&ref16, 1); + silc_atomic_init32(&ref32, 1); + silc_atomic_init_pointer(&refptr, SILC_32_TO_PTR(0xdeadbeef)); - ret8 = silc_atomic_add_int(&ref, 7); + ret8 = silc_atomic_add_int8(&ref8, 7); SILC_LOG_DEBUG(("ref8: 1 + 7 = %d (8)", ret8)); - ret8 = silc_atomic_add_int(&ref, 3); + ret8 = silc_atomic_add_int8(&ref8, 3); SILC_LOG_DEBUG(("ref8: 8 + 3 = %d (11)", ret8)); - ret8 = silc_atomic_sub_int(&ref, 10); + ret8 = silc_atomic_sub_int8(&ref8, 10); SILC_LOG_DEBUG(("ref8: 11 - 10 = %d (1)", ret8)); - ret16 = silc_atomic_add_int(&ref, 1); + ret16 = silc_atomic_add_int16(&ref16, 1); SILC_LOG_DEBUG(("ref16: 1 + 1 = %d (2)", ret16)); - ret16 = silc_atomic_add_int(&ref, 31020); + ret16 = silc_atomic_add_int16(&ref16, 31020); SILC_LOG_DEBUG(("ref16: 2 + 31020 = %d (31022)", ret16)); - ret16 = silc_atomic_add_int(&ref, 34000); + ret16 = silc_atomic_add_int16(&ref16, 34000); SILC_LOG_DEBUG(("ref16: 31022 + 34000 = %d (65022)", ret16)); - ret16 = silc_atomic_sub_int(&ref, 0); + ret16 = silc_atomic_sub_int16(&ref16, 0); SILC_LOG_DEBUG(("ref16: 65022 - 0 = %d (65022)", ret16)); - ret16 = silc_atomic_sub_int(&ref, 0xffff); - SILC_LOG_DEBUG(("ref16: 65022 - 0xffff = %d (65023) (underflow)", ret16)); + ret16 = silc_atomic_sub_int16(&ref16, 0xffff); + SILC_LOG_DEBUG(("ref16: 65022 - 0xfff = %d (65023) (underflow)", ret16)); - SILC_LOG_DEBUG(("Current value: %d (-513)", silc_atomic_get_int(&ref))); + SILC_LOG_DEBUG(("Current value: %d (-513)", + (SilcInt16)silc_atomic_get_int16(&ref16))); - SILC_LOG_DEBUG(("Swapping -513 with 8739200")); - if (!silc_atomic_cas(&ref, silc_atomic_get_int(&ref), 8739200)) + SILC_LOG_DEBUG(("Swapping -513 with 57392")); + if (!silc_atomic_cas16(&ref16, silc_atomic_get_int16(&ref16), 57392)) goto err; - SILC_LOG_DEBUG(("Current value: %d (8739200)", silc_atomic_get_int(&ref))); + SILC_LOG_DEBUG(("Current value: %d (57392)", + silc_atomic_get_int16(&ref16))); + SILC_LOG_DEBUG(("Swapping 57392 with -500")); + if (!silc_atomic_cas16(&ref16, silc_atomic_get_int16(&ref16), -500)) + goto err; + SILC_LOG_DEBUG(("Current value: %d (-500)", + (SilcInt16)silc_atomic_get_int16(&ref16))); + + ret32 = silc_atomic_add_int32(&ref32, 1); + SILC_LOG_DEBUG(("ref32: 1 + 1 = %d (2)", ret32)); + ret32 = silc_atomic_add_int32(&ref32, 310200); + SILC_LOG_DEBUG(("ref32: 2 + 310200 = %d (310202)", ret32)); + ret32 = silc_atomic_add_int32(&ref32, 34000000); + SILC_LOG_DEBUG(("ref32: 310202 + 34000000 = %d (34310202)", ret32)); + ret32 = silc_atomic_sub_int32(&ref32, 0); + SILC_LOG_DEBUG(("ref32: 34310202 - 0 = %d (34310202)", ret32)); + ret32 = silc_atomic_sub_int32(&ref32, 0xfffffff); + SILC_LOG_DEBUG(("ref32: 34310202 - 0xfffffff = %d (-234125253) " + "(underflow)", ret32)); + + SILC_LOG_DEBUG(("Current value: %d (-234125253)", + silc_atomic_get_int32(&ref32))); + + SILC_LOG_DEBUG(("Swapping -234125253 with 76327681")); + if (!silc_atomic_cas32(&ref32, silc_atomic_get_int32(&ref32), 76327681)) + goto err; + SILC_LOG_DEBUG(("Current value: %d (76327681)", + silc_atomic_get_int32(&ref32))); + + SILC_LOG_DEBUG(("Current ptr: %p (0xdeadbeef)", + silc_atomic_get_pointer(&refptr))); + SILC_LOG_DEBUG(("Swapping %p with NULL", silc_atomic_get_pointer(&refptr))); + if (!silc_atomic_cas_pointer(&refptr, + silc_atomic_get_pointer(&refptr), NULL)) + goto err; + SILC_LOG_DEBUG(("Current ptr: %p (NULL)", + silc_atomic_get_pointer(&refptr))); - silc_atomic_uninit(&ref); + silc_atomic_uninit8(&ref8); + silc_atomic_uninit16(&ref16); + silc_atomic_uninit32(&ref32); + silc_atomic_uninit_pointer(&refptr); success = TRUE; -- 2.43.0