/* silcatomic.h Author: Pekka Riikonen Copyright (C) 2006 - 2007 Pekka Riikonen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. */ /****h* silcutil/SILC Atomic Operations Interface * * DESCRIPTION * * SILC Atomic operations interface provides utility functions to perform * 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. This interface supports 8, 16 and 32 bit integers * and 32 or 64 bit pointers. * * 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 /* For now we always assume SMP */ #define SILC_SMP 1 /* Use lock prefix only on true SMP systems */ #ifdef SILC_SMP #define SILC_SMP_LOCK "lock; " #else #define SILC_SMP_LOCK #endif /* SILC_SMP */ /****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 { ... } SilcAtomic8; * * DESCRIPTION * * The atomic operation structure given as argument to all atomic * operation functions. It hols the actual 8-bit atomic variable. * * EXAMPLE * * SilcAtomic8 refcnt; * * // Initialize atomic variable * silc_atomic_init8(&refcnt, 0); * * ... * // Increment referene counter * 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_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 { volatile SilcUInt32 value; } SilcAtomic32; typedef struct { volatile void *value; } SilcAtomicPointer; #else #define SILC_ATOMIC_MUTEX typedef struct { SilcMutex lock; volatile SilcUInt32 value; } SilcAtomic32; typedef struct { SilcMutex lock; volatile void *value; } 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; volatile SilcUInt8 value; } SilcAtomic8; #endif /****f* silcutil/SilcAtomicAPI/silc_atomic_init32 * * SYNOPSIS * * static inline * 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_uninit32 function. * ***/ /****f* silcutil/SilcAtomicAPI/silc_atomic_init16 * * SYNOPSIS * * static inline * SilcBool silc_atomic_init16(SilcAtomic16 *atomic, SilcUInt16 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_uninit32 function. * ***/ /****f* silcutil/SilcAtomicAPI/silc_atomic_init8 * * SYNOPSIS * * static inline * SilcBool silc_atomic_init8(SilcAtomic8 *atomic, SilcUInt8 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_uninit8 function. * ***/ /****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. * ***/ #define SILC_ATOMIC_INIT_F(name, bits, type) \ static inline \ SilcBool silc_atomic_init##name(SilcAtomic##bits *atomic, type value) #if defined(SILC_ATOMIC_MUTEX) #define SILC_ATOMIC_INIT(name, bits, type) \ SILC_ATOMIC_INIT_F(name, bits, type) \ { \ atomic->value = value; \ return silc_mutex_alloc(&atomic->lock); \ } #else #define SILC_ATOMIC_INIT(name, bits, type) \ SILC_ATOMIC_INIT_F(name, bits, type) \ { \ atomic->value = value; \ return TRUE; \ } #endif /* SILC_ATOMIC_MUTEX */ SILC_ATOMIC_INIT(8, 8, SilcUInt8); SILC_ATOMIC_INIT(16, 16, SilcUInt16); SILC_ATOMIC_INIT(32, 32, SilcUInt32); SILC_ATOMIC_INIT(_pointer, Pointer, void *); /****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. * ***/ /****f* silcutil/SilcAtomicAPI/silc_atomic_uninit16 * * SYNOPSIS * * static inline * void silc_atomic_uninit16(SilcAtomic16 *atomic); * * DESCRIPTION * * Uninitializes the atomic variable `atomic'. This should alwyas be * called after the atomic variable is not used anymore. * ***/ /****f* silcutil/SilcAtomicAPI/silc_atomic_uninit8 * * SYNOPSIS * * static inline * void silc_atomic_uninit8(SilcAtomic8 *atomic); * * DESCRIPTION * * Uninitializes the atomic variable `atomic'. This should alwyas be * called after the atomic variable is not used anymore. * ***/ /****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. * ***/ #define SILC_ATOMIC_UNINIT_F(bits, t) \ static inline void silc_atomic_uninit##bits(SilcAtomic##t *atomic) #if defined(SILC_ATOMIC_MUTEX) #define SILC_ATOMIC_UNINIT(bits, t) \ SILC_ATOMIC_UNINIT_F(bits, t) \ { \ silc_mutex_free(atomic->lock); \ } #else #define SILC_ATOMIC_UNINIT(bits, t) \ SILC_ATOMIC_UNINIT_F(bits, t) \ { \ } #endif /* SILC_ATOMIC_MUTEX */ SILC_ATOMIC_UNINIT(8, 8); SILC_ATOMIC_UNINIT(16, 16); SILC_ATOMIC_UNINIT(32, 32); SILC_ATOMIC_UNINIT(_pointer, Pointer); /****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. * ***/ /****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. * ***/ /****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. * ***/ #define SILC_ATOMIC_SET_INT_F(bits) \ static inline void silc_atomic_set_int##bits(SilcAtomic##bits *atomic, \ SilcUInt##bits value) #if !defined(SILC_THREADS) #define SILC_ATOMIC_SET_INT(bits, bp, bp2) \ SILC_ATOMIC_SET_INT_F(bits) \ { \ /* No atomic operations */ \ atomic->value = value; \ } #elif defined(SILC_WIN32) #define SILC_ATOMIC_SET_INT(bits, bp, bp2) \ SILC_ATOMIC_SET_INT_F(bits) \ { \ /* Windows */ \ InterlockedExchange((LONG)&atomic->value, (LONG)value); \ } #elif defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64)) #define SILC_ATOMIC_SET_INT(bits, bp, bp2) \ SILC_ATOMIC_SET_INT_F(bits) \ { \ /* GCC + i486 or x86_64 */ \ __asm __volatile("xchg" bp " %" bp2 "0, %1" \ : "=r" (value) \ : "m" (atomic->value), "0" (value)); \ } #elif defined(__GNUC__) && defined(SILC_IA64) #define SILC_ATOMIC_SET_INT(bits, bp, bp2) \ SILC_ATOMIC_SET_INT_F(bits) \ { \ /* IA64, memory barrier needed */ \ atomic->value = value; \ __sync_synchronize(); \ } #elif defined(__GNUC__) && defined(SILC_POWERPC) #define SILC_ATOMIC_SET_INT(bits, bp, bp2) \ SILC_ATOMIC_SET_INT_F(bits) \ { \ /* PowerPC, memory barrier needed */ \ atomic->value = value; \ __asm("sync" : : : "memory"); \ } #else /* SILC_ATOMIC_MUTEX */ #define SILC_ATOMIC_SET_INT(bits, bp, bp2) \ SILC_ATOMIC_SET_INT_F(bits) \ { \ /* Mutex */ \ silc_mutex_lock(atomic->lock); \ atomic->value = value; \ silc_mutex_unlock(atomic->lock); \ } #endif /* !SILC_THREADS */ SILC_ATOMIC_SET_INT(8, "b", "b"); SILC_ATOMIC_SET_INT(16, "w", "w"); SILC_ATOMIC_SET_INT(32, "l", ""); /****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(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64))) /* No threads, Windows, i486 or x86_64, no memory barrier needed */ atomic->value = pointer; #elif defined(SILC_WIN32) InterlockedExchangePointer(&atomic->value, pointer); #elif defined(__GNUC__) && defined(SILC_IA64) /* IA64, memory barrier needed */ atomic->value = pointer; __sync_synchronize(); #elif defined(__GNUC__) && defined(SILC_POWERPC) /* PowerPC, memory barrier needed */ atomic->value = pointer; __asm("sync" : : : "memory"); #else /* Mutex */ silc_mutex_lock(atomic->lock); atomic->value = 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. * ***/ /****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. * ***/ /****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. * ***/ #define SILC_ATOMIC_GET_INT_F(bits) \ static inline \ SilcUInt##bits silc_atomic_get_int##bits(SilcAtomic##bits *atomic) #if !defined(SILC_THREADS) || defined(SILC_WIN32) || \ (defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64))) #define SILC_ATOMIC_GET_INT(bits) \ SILC_ATOMIC_GET_INT_F(bits) \ { \ SilcUInt##bits ret; \ \ /* No threads, Windows, i486 or x86_64, no memory barrier needed */ \ ret = atomic->value; \ return ret; \ } #elif defined(__GNUC__) && defined(SILC_IA64) #define SILC_ATOMIC_GET_INT(bits) \ SILC_ATOMIC_GET_INT_F(bits) \ { \ SilcUInt##bits ret; \ \ /* IA64, memory barrier needed */ \ __sync_synchronize(); \ ret = atomic->value; \ return ret; \ } #elif defined(__GNUC__) && defined(SILC_POWERPC) #define SILC_ATOMIC_GET_INT(bits) \ SILC_ATOMIC_GET_INT_F(bits) \ { \ SilcUInt##bits ret; \ \ /* PowerPC, memory barrier needed */ \ __asm("sync" : : : "memory"); \ ret = atomic->value; \ return ret; \ } #else /* SILC_ATOMIC_MUTEX */ #define SILC_ATOMIC_GET_INT(bits) \ SILC_ATOMIC_GET_INT_F(bits) \ { \ SilcUInt##bits ret; \ \ /* Mutex */ \ silc_mutex_lock(atomic->lock); \ ret = atomic->value; \ silc_mutex_unlock(atomic->lock); \ return ret; \ } #endif /* !SILC_THREADS */ SILC_ATOMIC_GET_INT(8); SILC_ATOMIC_GET_INT(16); SILC_ATOMIC_GET_INT(32); /****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->value; return ret; #elif defined(__GNUC__) && defined(SILC_IA64) /* IA64, memory barrier needed */ __sync_synchronize(); ret = (void *)atomic->value; return ret; #elif defined(__GNUC__) && defined(SILC_POWERPC) /* PowerPC, memory barrier needed */ __asm("sync" : : : "memory"); ret = (void *)atomic->value; return ret; #else /* Mutex */ silc_mutex_lock(atomic->lock); ret = (void *)atomic->value; 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. * ***/ /****f* silcutil/SilcAtomicAPI/silc_atomic_add_int16 * * 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. * ***/ /****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. * ***/ #define SILC_ATOMIC_ADD_INT_F(bits) \ static inline \ SilcUInt##bits silc_atomic_add_int##bits(SilcAtomic##bits *atomic, \ SilcInt##bits value) #if !defined(SILC_THREADS) #define SILC_ATOMIC_ADD_INT(bits, bp) \ SILC_ATOMIC_ADD_INT_F(bits) \ { \ SilcUInt##bits ret; \ /* No atomic operations */ \ ret = atomic->value; \ atomic->value += value; \ return ret + value; \ } #elif defined(SILC_WIN32) #define SILC_ATOMIC_ADD_INT(bits, bp) \ SILC_ATOMIC_ADD_INT_F(bits) \ { \ SilcUInt##bits ret; \ LONG val = value; \ /* Windows */ \ ret = InterlockedExchangeAdd(&atomic->value, val); \ return ret + value; \ } #elif defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64)) #define SILC_ATOMIC_ADD_INT(bits, bp) \ SILC_ATOMIC_ADD_INT_F(bits) \ { \ SilcUInt##bits ret; \ /* GCC + i486 or x86_64 */ \ __asm __volatile(SILC_SMP_LOCK "xadd" bp " %0, %1" \ : "=r" (ret), "+m" (atomic->value) \ : "0" (value)); \ return ret + value; \ } #elif defined(__GNUC__) && defined(SILC_IA64) #define SILC_ATOMIC_ADD_INT(bits, bp) \ SILC_ATOMIC_ADD_INT_F(bits) \ { \ SilcUInt##bits ret; \ SilcInt32 val = value; /* GCC + IA64 (GCC builtin atomic operations) */ \ ret = __sync_fetch_and_add(&atomic->value, val); \ return ret + value; \ } #elif defined(__GNUC__) && defined(SILC_POWERPC) #define SILC_ATOMIC_ADD_INT(bits, bp) \ SILC_ATOMIC_ADD_INT_F(bits) \ { \ SilcUInt32 ret; \ SilcInt32 val = value; \ /* 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" (val), "r" (&atomic->value) \ : "cc"); \ return ret; \ } #else /* SILC_ATOMIC_MUTEX */ #define SILC_ATOMIC_ADD_INT(bits, bp) \ SILC_ATOMIC_ADD_INT_F(bits) \ { \ SilcUInt##bits ret; \ /* Mutex */ \ silc_mutex_lock(atomic->lock); \ ret = atomic->value; \ atomic->value += value; \ silc_mutex_unlock(atomic->lock); \ return ret + value; \ } #endif /* !SILC_THREADS */ SILC_ATOMIC_ADD_INT(8, "b"); SILC_ATOMIC_ADD_INT(16, "w"); SILC_ATOMIC_ADD_INT(32, "l"); /****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. * ***/ /****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. * ***/ /****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. * ***/ #define silc_atomic_sub_int8(a, v) silc_atomic_add_int8(a, (-v)) #define silc_atomic_sub_int16(a, v) silc_atomic_add_int16(a, (-v)) #define silc_atomic_sub_int32(a, v) silc_atomic_add_int32(a, (-v)) /****f* silcutil/SilcAtomicAPI/silc_atomic_inc32 * * SYNOPSIS * * static inline * void silc_atomic_inc32(SilcAtomic32 *atomic); * * DESCRIPTION * * Atomically increments 32-bit integer by one. * ***/ /****f* silcutil/SilcAtomicAPI/silc_atomic_inc16 * * SYNOPSIS * * static inline * void silc_atomic_inc16(SilcAtomic16 *atomic); * * DESCRIPTION * * Atomically increments 16-bit integer by one. * ***/ /****f* silcutil/SilcAtomicAPI/silc_atomic_inc8 * * SYNOPSIS * * static inline * void silc_atomic_inc8(SilcAtomic8 *atomic); * * DESCRIPTION * * Atomically increments 8-bit integer by one. * ***/ #define SILC_ATOMIC_INC_F(bits) \ static inline void silc_atomic_inc##bits(SilcAtomic##bits *atomic) #if !defined(SILC_THREADS) #define SILC_ATOMIC_INC(bits, bp) \ SILC_ATOMIC_INC_F(bits) \ { \ /* No atomic operations */ \ ++atomic->value; \ } #elif defined(SILC_WIN32) #define SILC_ATOMIC_INC(bits, bp) \ SILC_ATOMIC_INC_F(bits) \ { \ /* Windows */ \ InterlockedIncrement((LONG)&atomic->value); \ } #elif defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64)) #define SILC_ATOMIC_INC(bits, bp) \ SILC_ATOMIC_INC_F(bits) \ { \ /* GCC + i486 or x86_64 */ \ __asm __volatile(SILC_SMP_LOCK "inc" bp " %0" \ : "+m" (atomic->value)); \ } #elif defined(__GNUC__) && defined(SILC_IA64) #define SILC_ATOMIC_INC(bits, bp) \ SILC_ATOMIC_INC_F(bits) \ { \ /* GCC + IA64 (GCC builtin atomic operations) */ \ __sync_fetch_and_add(&atomic->value, 1); \ } #elif defined(__GNUC__) && defined(SILC_POWERPC) #define SILC_ATOMIC_INC(bits, bp) \ SILC_ATOMIC_INC_F(bits) \ { \ SilcUInt32 ret; \ SilcInt32 val = 1; \ /* 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" (val), "r" (&atomic->value) \ : "cc"); \ } #else /* SILC_ATOMIC_MUTEX */ #define SILC_ATOMIC_INC(bits, bp) \ SILC_ATOMIC_INC_F(bits) \ { \ /* Mutex */ \ silc_mutex_lock(atomic->lock); \ ++atomic->value; \ silc_mutex_unlock(atomic->lock); \ } #endif /* !SILC_THREADS */ SILC_ATOMIC_INC(8, "b"); SILC_ATOMIC_INC(16, "w"); SILC_ATOMIC_INC(32, "l"); /****f* silcutil/SilcAtomicAPI/silc_atomic_dec32 * * SYNOPSIS * * static inline * void silc_atomic_dec32(SilcAtomic32 *atomic); * * DESCRIPTION * * Atomically decrements 32-bit integer by one. * ***/ /****f* silcutil/SilcAtomicAPI/silc_atomic_dec16 * * SYNOPSIS * * static inline * void silc_atomic_dec16(SilcAtomic16 *atomic); * * DESCRIPTION * * Atomically decrements 16-bit integer by one. * ***/ /****f* silcutil/SilcAtomicAPI/silc_atomic_dec8 * * SYNOPSIS * * static inline * void silc_atomic_dec8(SilcAtomic8 *atomic); * * DESCRIPTION * * Atomically decrements 8-bit integer by one. * ***/ #define SILC_ATOMIC_DEC_F(bits) \ static inline void silc_atomic_dec##bits(SilcAtomic##bits *atomic) #if !defined(SILC_THREADS) #define SILC_ATOMIC_DEC(bits, bp) \ SILC_ATOMIC_DEC_F(bits) \ { \ /* No atomic operations */ \ --atomic->value; \ } #elif defined(SILC_WIN32) #define SILC_ATOMIC_DEC(bits, bp) \ SILC_ATOMIC_DEC_F(bits) \ { \ /* Windows */ \ InterlockedDecrement((LONG)&atomic->value); \ } #elif defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64)) #define SILC_ATOMIC_DEC(bits, bp) \ SILC_ATOMIC_DEC_F(bits) \ { \ /* GCC + i486 or x86_64 */ \ __asm __volatile(SILC_SMP_LOCK "dec" bp " %0" \ : "+m" (atomic->value)); \ } #elif defined(__GNUC__) && defined(SILC_IA64) #define SILC_ATOMIC_DEC(bits, bp) \ SILC_ATOMIC_DEC_F(bits) \ { \ /* GCC + IA64 (GCC builtin atomic operations) */ \ __sync_fetch_and_sub(&atomic->value, 1); \ } #elif defined(__GNUC__) && defined(SILC_POWERPC) #define SILC_ATOMIC_DEC(bits, bp) \ SILC_ATOMIC_DEC_F(bits) \ { \ SilcUInt32 ret; \ SilcInt32 val = -1; \ /* 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" (val), "r" (&atomic->value) \ : "cc"); \ } #else /* SILC_ATOMIC_MUTEX */ #define SILC_ATOMIC_DEC(bits, bp) \ SILC_ATOMIC_DEC_F(bits) \ { \ /* Mutex */ \ silc_mutex_lock(atomic->lock); \ --atomic->value; \ silc_mutex_unlock(atomic->lock); \ } #endif /* !SILC_THREADS */ SILC_ATOMIC_DEC(8, "b"); SILC_ATOMIC_DEC(16, "w"); SILC_ATOMIC_DEC(32, "l"); /****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. * ***/ /****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. * ***/ /****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. * ***/ #define SILC_ATOMIC_CAS_F(bits) \ static inline SilcBool silc_atomic_cas##bits(SilcAtomic##bits *atomic, \ SilcInt##bits old_val, \ SilcInt##bits new_val) #if !defined(SILC_THREADS) #define SILC_ATOMIC_CAS(bits, bp) \ SILC_ATOMIC_CAS_F(bits) \ { \ /* No atomic operations */ \ if (atomic->value == (SilcUInt##bits)old_val) { \ atomic->value = new_val; \ return TRUE; \ } \ return FALSE; \ } #elif defined(SILC_WIN32) #define SILC_ATOMIC_CAS(bits, bp) \ SILC_ATOMIC_CAS_F(bits) \ { \ /* 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)) #define SILC_ATOMIC_CAS(bits, bp) \ SILC_ATOMIC_CAS_F(bits) \ { \ /* GCC + i486 or x86_64 */ \ SilcUInt##bits ret; \ __asm __volatile(SILC_SMP_LOCK "cmpxchg" bp " %2, %1" \ : "=a" (ret), "=m" (atomic->value) \ : "r" (new_val), "m" (atomic->value), "0" (old_val)); \ return ret == (SilcUInt##bits)old_val; \ } #elif defined(__GNUC__) && defined(SILC_IA64) #define SILC_ATOMIC_CAS(bits, bp) \ SILC_ATOMIC_CAS_F(bits) \ { \ /* 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) #define SILC_ATOMIC_CAS(bits, bp) \ SILC_ATOMIC_CAS_F(bits) \ { \ /* GCC + PowerPC */ \ /* XXX TODO */ \ } #else /* SILC_ATOMIC_MUTEX */ #define SILC_ATOMIC_CAS(bits, bp) \ SILC_ATOMIC_CAS_F(bits) \ { \ /* Mutex */ \ silc_mutex_lock(atomic->lock); \ if (atomic->value == (SilcUInt##bits)old_val) { \ atomic->value = new_val; \ silc_mutex_unlock(atomic->lock); \ return TRUE; \ } \ silc_mutex_unlock(atomic->lock); \ return FALSE; \ } #endif /* !SILC_THREADS */ SILC_ATOMIC_CAS(8, "b"); SILC_ATOMIC_CAS(16, "w"); SILC_ATOMIC_CAS(32, "l"); /****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) { #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 InterlockedCompareExchangePointer(&atomic->value, n, o) == o; #elif defined(__GNUC__) && defined(SILC_I486) /* GCC + i486 */ void *ret; __asm __volatile(SILC_SMP_LOCK "cmpxchgl %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_X86_64) /* GCC + x86_64 */ void *ret; __asm __volatile(SILC_SMP_LOCK "cmpxchgq %q2, %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) */ return __sync_bool_compare_and_swap((long)&atomic->value, (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->value == old_val) { atomic->value = new_val; silc_mutex_unlock(atomic->lock); return TRUE; } silc_mutex_unlock(atomic->lock); return FALSE; #endif } #endif /* SILCATOMIC_H */