--- /dev/null
+/*
+
+ silcatomic.h
+
+ Author: Pekka Riikonen <priikone@silcnet.org>
+
+ 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 */
--- /dev/null
+/* 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;
+}