Added silcatomic.h for atomic operations.
authorPekka Riikonen <priikone@silcnet.org>
Wed, 20 Sep 2006 17:11:59 +0000 (17:11 +0000)
committerPekka Riikonen <priikone@silcnet.org>
Wed, 20 Sep 2006 17:11:59 +0000 (17:11 +0000)
lib/silcutil/silcatomic.h [new file with mode: 0644]
lib/silcutil/tests/Makefile.am
lib/silcutil/tests/test_silcatomic.c [new file with mode: 0644]

diff --git a/lib/silcutil/silcatomic.h b/lib/silcutil/silcatomic.h
new file mode 100644 (file)
index 0000000..02a71ca
--- /dev/null
@@ -0,0 +1,262 @@
+/*
+
+  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 */
index 4ce237bc592412b0b0880107eee8229419e04929..6a57f7887a538e0c0ab310966ba0c30ec354403d 100644 (file)
@@ -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 (file)
index 0000000..dc2bac6
--- /dev/null
@@ -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;
+}