5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2006 Pekka Riikonen
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; version 2 of the License.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
20 /****h* silcutil/SILC Atomic Operations Interface
24 * SILC Atomic operations interface provides utility functions to perform
25 * simple operations with integers atomically. This enables fast integer
26 * additions and subtractions safely in multithreaded environment. It is
27 * especially suited for reference counters and similar and is much faster
30 * If threads were not enabled when compiling the source code, the operations
31 * are not atomic. On some platforms this interface actually use mutual
32 * exclusion lock instead of true atomic operations, leading into some
40 /****s* silcutil/SilcAtomicAPI/SilcAtomic
44 * typedef struct { ... } SilcAtomic;
48 * The atomic operation structure given as argument to all atomic
49 * operation functions. It hols the actual atomic variable. On most
50 * platforms its size is 32 bits but on some platforms it may be
57 * // Initialize atomic variable
58 * silc_atomic_init(&refcnt, 0);
61 * // Increment referene counter
62 * silc_atomic_add_int(&refcnt, 1);
65 * // Uninitialize atomic variable
66 * silc_atomic_uninit(&refcnt);
69 #if !defined(SILC_THREADS) || defined(SILC_WIN32) || (defined(__GNUC__) && \
70 (defined(SILC_I486) || defined(SILC_X86_64) || defined(SILC_IA64) || \
71 defined(SILC_POWERPC)))
74 volatile SilcUInt32 value;
77 #define SILC_ATOMIC_MUTEX
80 volatile SilcUInt32 value;
85 /****f* silcutil/SilcAtomicAPI/silc_atomic_init
90 * SilcBool silc_atomic_init(SilcAtomic *atomic, int value);
94 * Initializes the atomic variable `atomic', and sets the `value' as its
95 * inital value. Returns FALSE on error. To uninitialize call the
96 * silc_atomic_uninit function.
101 SilcBool silc_atomic_init(SilcAtomic *atomic, int value)
103 atomic->value = value;
105 #if defined(SILC_ATOMIC_MUTEX)
106 if (!silc_mutex_alloc(&atomic->lock))
108 #endif /* SILC_ATOMIC_MUTEX */
113 /****f* silcutil/SilcAtomicAPI/silc_atomic_uninit
118 * void silc_atomic_uninit(SilcAtomic *atomic);
122 * Uninitializes the atomic variable `atomic'. This should alwyas be
123 * called after the atomic variable is not used anymore.
128 void silc_atomic_uninit(SilcAtomic *atomic)
131 #if defined(SILC_ATOMIC_MUTEX)
132 silc_mutex_free(atomic->lock);
133 #endif /* SILC_ATOMIC_MUTEX */
136 /****f* silcutil/SilcAtomicAPI/silc_atomic_add_int
141 * SilcUInt32 silc_atomic_add_int(SilcAtomic *atomic, int value);
145 * Atomically adds `value' to 32-bit integer. Returns the value after
151 SilcUInt32 silc_atomic_add_int(SilcAtomic *atomic, int value)
155 #if !defined(SILC_THREADS)
156 /* No atomic operations */
158 atomic->value += value;
160 #elif defined(SILC_WIN32)
162 ret = InterlockedExchangeAdd(&atomic->value, (LONG)value);
164 #elif defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64))
165 /* GCC + i486 or x86_64 */
166 __asm __volatile("lock; xaddl %0, %1"
167 : "=r" (ret), "+m" (atomic->value)
170 #elif defined(__GNUC__) && defined(SILC_IA64)
171 /* GCC + IA64 (GCC builtin atomic operations) */
172 ret = __sync_fetch_and_add(&atomic->value, value);
174 #elif defined(__GNUC__) && defined(SILC_POWERPC)
175 /* GCC + PowerPC (code adapted from IBM's documentation) */
176 /* XXX Hmm.. should I sync and isync this?... */
177 __asm __volatile("0: lwarx %0, 0, %2\n"
179 " stwcx. %0, 0, %2\n"
182 : "r" (value), "r" (&atomic->value)
188 silc_mutex_lock(atomic->lock);
190 atomic->value += value;
191 silc_mutex_unlock(atomic->lock);
197 /****f* silcutil/SilcAtomicAPI/silc_atomic_sub_int
202 * SilcUInt32 silc_atomic_sub_int(SilcAtomic *atomic, int value);
206 * Atomically subtracts `value' from 32-bit integer. Returns the value
212 SilcUInt32 silc_atomic_sub_int(SilcAtomic *atomic, int value)
214 return silc_atomic_add_int(atomic, -value);
217 /****f* silcutil/SilcAtomicAPI/silc_atomic_get_int
222 * SilcUInt32 silc_atomic_get_int(SilcAtomic *atomic);
226 * Returns the current value of the atomic variable.
231 SilcUInt32 silc_atomic_get_int(SilcAtomic *atomic)
235 #if !defined(SILC_THREADS) || defined(SILC_WIN32) || \
236 (defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64)))
237 /* No threads, Windows, i486 or x86_64, no memory barrier needed */
241 #elif defined(__GNUC__) && defined(SILC_IA64)
242 /* IA64, memory barrier needed */
243 __sync_synchronize();
247 #elif defined(__GNUC__) && defined(SILC_POWERPC)
248 /* PowerPC, memory barrier needed */
249 __asm("sync" : : : "memory");
255 silc_mutex_lock(atomic->lock);
257 silc_mutex_unlock(atomic->lock);
262 #endif /* SILCATOMIC_H */