From: Pekka Riikonen Date: Wed, 20 Sep 2006 17:11:59 +0000 (+0000) Subject: Added silcatomic.h for atomic operations. X-Git-Tag: 1.2.beta1~704 X-Git-Url: http://git.silcnet.org/gitweb/?p=crypto.git;a=commitdiff_plain;h=110f33c7fb5aa691ab3afa9e8371ac7e7a540d21 Added silcatomic.h for atomic operations. --- diff --git a/lib/silcutil/silcatomic.h b/lib/silcutil/silcatomic.h new file mode 100644 index 00000000..02a71ca9 --- /dev/null +++ b/lib/silcutil/silcatomic.h @@ -0,0 +1,262 @@ +/* + + silcatomic.h + + Author: Pekka Riikonen + + Copyright (C) 2006 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. + * + * 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. + * + ***/ + +#ifndef SILCATOMIC_H +#define SILCATOMIC_H + +/****s* silcutil/SilcAtomicAPI/SilcAtomic + * + * NAME + * + * typedef struct { ... } SilcAtomic; + * + * 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. + * + * EXAMPLE + * + * SilcAtomic refcnt; + * + * // Initialize atomic variable + * silc_atomic_init(&refcnt, 0); + * + * ... + * // Increment referene counter + * silc_atomic_add_int(&refcnt, 1); + * ... + * + * // Uninitialize atomic variable + * silc_atomic_uninit(&refcnt); + * + ***/ +#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; +} SilcAtomic; +#else +#define SILC_ATOMIC_MUTEX +typedef struct +{ + volatile SilcUInt32 value; + SilcMutex lock; +} SilcAtomic; +#endif + +/****f* silcutil/SilcAtomicAPI/silc_atomic_init + * + * SYNOPSIS + * + * static inline + * SilcBool silc_atomic_init(SilcAtomic *atomic, int 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. + * + ***/ + +static inline +SilcBool silc_atomic_init(SilcAtomic *atomic, int value) +{ + atomic->value = value; + +#if defined(SILC_ATOMIC_MUTEX) + if (!silc_mutex_alloc(&atomic->lock)) + return FALSE; +#endif /* SILC_ATOMIC_MUTEX */ + + return TRUE; +} + +/****f* silcutil/SilcAtomicAPI/silc_atomic_uninit + * + * SYNOPSIS + * + * static inline + * void silc_atomic_uninit(SilcAtomic *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(SilcAtomic *atomic) +{ + atomic->value = 0; +#if defined(SILC_ATOMIC_MUTEX) + silc_mutex_free(atomic->lock); +#endif /* SILC_ATOMIC_MUTEX */ +} + +/****f* silcutil/SilcAtomicAPI/silc_atomic_add_int + * + * SYNOPSIS + * + * static inline + * SilcUInt32 silc_atomic_add_int(SilcAtomic *atomic, int value); + * + * DESCRIPTION + * + * Atomically adds `value' to 32-bit integer. Returns the value after + * addition. + * + ***/ + +static inline +SilcUInt32 silc_atomic_add_int(SilcAtomic *atomic, int 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) */ + /* 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; + +#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_int + * + * SYNOPSIS + * + * static inline + * SilcUInt32 silc_atomic_sub_int(SilcAtomic *atomic, int value); + * + * DESCRIPTION + * + * Atomically subtracts `value' from 32-bit integer. Returns the value + * after subtraction. + * + ***/ + +static inline +SilcUInt32 silc_atomic_sub_int(SilcAtomic *atomic, int value) +{ + return silc_atomic_add_int(atomic, -value); +} + +/****f* silcutil/SilcAtomicAPI/silc_atomic_get_int + * + * SYNOPSIS + * + * static inline + * SilcUInt32 silc_atomic_get_int(SilcAtomic *atomic); + * + * DESCRIPTION + * + * Returns the current value of the atomic variable. + * + ***/ + +static inline +SilcUInt32 silc_atomic_get_int(SilcAtomic *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 +} + +#endif /* SILCATOMIC_H */ diff --git a/lib/silcutil/tests/Makefile.am b/lib/silcutil/tests/Makefile.am index 4ce237bc..6a57f788 100644 --- a/lib/silcutil/tests/Makefile.am +++ b/lib/silcutil/tests/Makefile.am @@ -19,7 +19,8 @@ AUTOMAKE_OPTIONS = 1.0 no-dependencies foreign bin_PROGRAMS = test_silcstrutil test_silcstringprep test_silchashtable \ test_silclist test_silcfsm test_silcasync test_silcschedule \ - test_silcnet test_silcstack test_silcmime test_silcfdstream + test_silcnet test_silcstack test_silcmime test_silcfdstream \ + test_silcatomic test_silcstrutil_SOURCES = test_silcstrutil.c test_silcstringprep_SOURCES = test_silcstringprep.c @@ -32,6 +33,7 @@ test_silcschedule_SOURCES = test_silcschedule.c test_silcnet_SOURCES = test_silcnet.c test_silcstack_SOURCES = test_silcstack.c test_silcfdstream_SOURCES = test_silcfdstream.c +test_silcatomic_SOURCES = test_silcatomic.c LIBS = $(SILC_COMMON_LIBS) LDADD = -L.. -L../.. -lsilc diff --git a/lib/silcutil/tests/test_silcatomic.c b/lib/silcutil/tests/test_silcatomic.c new file mode 100644 index 00000000..dc2bac6e --- /dev/null +++ b/lib/silcutil/tests/test_silcatomic.c @@ -0,0 +1,51 @@ +/* atomic operation tests */ + +#include "silc.h" +#include "silcatomic.h" + +int main(int argc, char **argv) +{ + SilcBool success = FALSE; + SilcAtomic ref; + SilcUInt8 ret8; + SilcUInt16 ret16; + SilcUInt32 ret32; + + if (argc > 1 && !strcmp(argv[1], "-d")) { + silc_log_debug(TRUE); + silc_log_debug_hexdump(TRUE); + silc_log_set_debug_string("*atomic*"); + } + + silc_atomic_init(&ref, 1); + + ret8 = silc_atomic_add_int(&ref, 7); + SILC_LOG_DEBUG(("ref8: 1 + 7 = %d (8)", ret8)); + ret8 = silc_atomic_add_int(&ref, 3); + SILC_LOG_DEBUG(("ref8: 8 + 3 = %d (11)", ret8)); + ret8 = silc_atomic_sub_int(&ref, 10); + SILC_LOG_DEBUG(("ref8: 11 - 10 = %d (1)", ret8)); + + ret16 = silc_atomic_add_int(&ref, 1); + SILC_LOG_DEBUG(("ref16: 1 + 1 = %d (2)", ret16)); + ret16 = silc_atomic_add_int(&ref, 31020); + SILC_LOG_DEBUG(("ref16: 2 + 31020 = %d (31022)", ret16)); + ret16 = silc_atomic_add_int(&ref, 34000); + SILC_LOG_DEBUG(("ref16: 31022 + 34000 = %d (65022)", ret16)); + ret16 = silc_atomic_sub_int(&ref, 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)); + + SILC_LOG_DEBUG(("Current value: %d (-513)", silc_atomic_get_int(&ref))); + + silc_atomic_uninit(&ref); + + success = TRUE; + + err: + SILC_LOG_DEBUG(("Testing was %s", success ? "SUCCESS" : "FAILURE")); + fprintf(stderr, "Testing was %s\n", success ? "SUCCESS" : "FAILURE"); + + return success; +}