5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2006 - 2007 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
28 * than using locking. This interface supports 8, 16 and 32 bit integers
29 * and 32 or 64 bit pointers.
31 * On some platforms this interface actually use mutual exclusion lock
32 * instead of true atomic operations, leading into some performace penalty.
33 * Also on some platforms the 8 and 16 bit integers are actually 32 bit
36 * Fast operations are supported on: x86, x86_64, ia64, PPC
40 * SilcAtomic32 refcnt;
42 * // Initialize atomic variable
43 * silc_atomic_init32(&refcnt, 0);
45 * // Increment referene counter by one
46 * silc_atomic_add_int32(&refcnt, 1);
48 * // Uninitialize atomic variable
49 * silc_atomic_uninit32(&refcnt);
56 /* Use lock prefix only on true SMP systems */
58 #define SILC_SMP_LOCK "lock; "
63 /****s* silcutil/SilcAtomicAPI/SilcAtomic32
67 * typedef struct { ... } SilcAtomic32;
71 * The atomic operation structure given as argument to all atomic
72 * operation functions. It hols the actual 32-bit atomic variable.
76 * SilcAtomic32 refcnt;
78 * // Initialize atomic variable
79 * silc_atomic_init32(&refcnt, 0);
82 * // Increment referene counter
83 * silc_atomic_add_int32(&refcnt, 1);
86 * // Uninitialize atomic variable
87 * silc_atomic_uninit32(&refcnt);
91 /****s* silcutil/SilcAtomicAPI/SilcAtomic16
95 * typedef struct { ... } SilcAtomic16;
99 * The atomic operation structure given as argument to all atomic
100 * operation functions. It hols the actual 16-bit atomic variable.
104 * SilcAtomic16 refcnt;
106 * // Initialize atomic variable
107 * silc_atomic_init16(&refcnt, 0);
110 * // Increment referene counter
111 * silc_atomic_add_int16(&refcnt, 1);
114 * // Uninitialize atomic variable
115 * silc_atomic_uninit16(&refcnt);
119 /****s* silcutil/SilcAtomicAPI/SilcAtomic8
123 * typedef struct { ... } SilcAtomic8;
127 * The atomic operation structure given as argument to all atomic
128 * operation functions. It hols the actual 8-bit atomic variable.
132 * SilcAtomic8 refcnt;
134 * // Initialize atomic variable
135 * silc_atomic_init8(&refcnt, 0);
138 * // Increment referene counter
139 * silc_atomic_add_int8(&refcnt, 1);
142 * // Uninitialize atomic variable
143 * silc_atomic_uninit8(&refcnt);
147 /****s* silcutil/SilcAtomicAPI/SilcAtomicPointer
151 * typedef struct { ... } SilcAtomicPointer;
155 * The atomic operation structure given as argument to all atomic
156 * operation functions. It hols the actual pointer variable.
160 * SilcAtomicPointer ptr;
162 * // Initialize atomic variable
163 * silc_atomic_init_pointer(&ptr, NULL);
167 * silc_atomic_set_pointer(&ptr, context);
170 * // Uninitialize atomic variable
171 * silc_atomic_uninit_pointer(&ptr);
175 #if !defined(SILC_THREADS) || defined(SILC_WIN32) || (defined(__GNUC__) && \
176 (defined(SILC_I486) || defined(SILC_X86_64) || defined(SILC_IA64) || \
177 defined(SILC_POWERPC)))
179 volatile SilcUInt32 value;
182 volatile void *value;
185 #define SILC_ATOMIC_MUTEX
188 volatile SilcUInt32 value;
192 volatile void *value;
196 #if !defined(SILC_THREADS) || (defined(__GNUC__) && (defined(SILC_I486) || \
197 defined(SILC_X86_64)))
199 volatile SilcUInt16 value;
201 #elif defined(SILC_WIN32) || (defined(__GNUC__) && (defined(SILC_IA64) || \
202 defined(SILC_POWERPC)))
204 volatile SilcUInt32 value;
209 volatile SilcUInt16 value;
213 #if !defined(SILC_THREADS) || (defined(__GNUC__) && (defined(SILC_I486) || \
214 defined(SILC_X86_64)))
216 volatile SilcUInt8 value;
218 #elif defined(SILC_WIN32) || (defined(__GNUC__) && (defined(SILC_IA64) || \
219 defined(SILC_POWERPC)))
221 volatile SilcUInt32 value;
226 volatile SilcUInt8 value;
230 /****f* silcutil/SilcAtomicAPI/silc_atomic_init32
235 * SilcBool silc_atomic_init32(SilcAtomic32 *atomic, SilcUInt32 value);
239 * Initializes the atomic variable `atomic', and sets the `value' as its
240 * inital value. Returns FALSE on error. To uninitialize call the
241 * silc_atomic_uninit32 function.
245 /****f* silcutil/SilcAtomicAPI/silc_atomic_init16
250 * SilcBool silc_atomic_init16(SilcAtomic16 *atomic, SilcUInt16 value);
254 * Initializes the atomic variable `atomic', and sets the `value' as its
255 * inital value. Returns FALSE on error. To uninitialize call the
256 * silc_atomic_uninit32 function.
260 /****f* silcutil/SilcAtomicAPI/silc_atomic_init8
265 * SilcBool silc_atomic_init8(SilcAtomic8 *atomic, SilcUInt8 value);
269 * Initializes the atomic variable `atomic', and sets the `value' as its
270 * inital value. Returns FALSE on error. To uninitialize call the
271 * silc_atomic_uninit8 function.
275 /****f* silcutil/SilcAtomicAPI/silc_atomic_init_pointer
280 * SilcBool silc_atomic_init_pointer(SilcAtomicPointer *atomic,
285 * Initializes the atomic pointer variable `atomic', and sets the `pointer'
286 * as its inital pointer. Returns FALSE on error. To uninitialize call
287 * the silc_atomic_uninit_pointer function.
291 #define SILC_ATOMIC_INIT_F(name, bits, type) \
293 SilcBool silc_atomic_init##name(SilcAtomic##bits *atomic, type value)
295 #if defined(SILC_ATOMIC_MUTEX)
296 #define SILC_ATOMIC_INIT(name, bits, type) \
297 SILC_ATOMIC_INIT_F(name, bits, type) \
299 atomic->value = value; \
300 return silc_mutex_alloc(&atomic->lock); \
303 #define SILC_ATOMIC_INIT(name, bits, type) \
304 SILC_ATOMIC_INIT_F(name, bits, type) \
306 atomic->value = value; \
309 #endif /* SILC_ATOMIC_MUTEX */
311 SILC_ATOMIC_INIT(8, 8, SilcUInt8)
312 SILC_ATOMIC_INIT(16, 16, SilcUInt16)
313 SILC_ATOMIC_INIT(32, 32, SilcUInt32)
314 SILC_ATOMIC_INIT(_pointer, Pointer, void *)
316 /****f* silcutil/SilcAtomicAPI/silc_atomic_uninit32
321 * void silc_atomic_uninit32(SilcAtomic32 *atomic);
325 * Uninitializes the atomic variable `atomic'. This should alwyas be
326 * called after the atomic variable is not used anymore.
330 /****f* silcutil/SilcAtomicAPI/silc_atomic_uninit16
335 * void silc_atomic_uninit16(SilcAtomic16 *atomic);
339 * Uninitializes the atomic variable `atomic'. This should alwyas be
340 * called after the atomic variable is not used anymore.
344 /****f* silcutil/SilcAtomicAPI/silc_atomic_uninit8
349 * void silc_atomic_uninit8(SilcAtomic8 *atomic);
353 * Uninitializes the atomic variable `atomic'. This should alwyas be
354 * called after the atomic variable is not used anymore.
358 /****f* silcutil/SilcAtomicAPI/silc_atomic_uninit_pointer
363 * void silc_atomic_uninit_pointer(SilcAtomicPointer *atomic);
367 * Uninitializes the atomic variable `atomic'. This should alwyas be
368 * called after the atomic variable is not used anymore.
372 #define SILC_ATOMIC_UNINIT_F(bits, t) \
373 static inline void silc_atomic_uninit##bits(SilcAtomic##t *atomic)
375 #if defined(SILC_ATOMIC_MUTEX)
376 #define SILC_ATOMIC_UNINIT(bits, t) \
377 SILC_ATOMIC_UNINIT_F(bits, t) \
379 silc_mutex_free(atomic->lock); \
382 #define SILC_ATOMIC_UNINIT(bits, t) \
383 SILC_ATOMIC_UNINIT_F(bits, t) \
385 memset(atomic, 0, sizeof(*atomic)); \
387 #endif /* SILC_ATOMIC_MUTEX */
389 SILC_ATOMIC_UNINIT(8, 8)
390 SILC_ATOMIC_UNINIT(16, 16)
391 SILC_ATOMIC_UNINIT(32, 32)
392 SILC_ATOMIC_UNINIT(_pointer, Pointer)
394 /****f* silcutil/SilcAtomicAPI/silc_atomic_set_int32
399 * void silc_atomic_set_int32(SilcAtomic32 *atomic, SilcUInt32 value);
403 * Atomically sets `value' to 32-bit integer.
407 /****f* silcutil/SilcAtomicAPI/silc_atomic_set_int16
412 * void silc_atomic_set_int16(SilcAtomic16 *atomic, SilcUInt16 value);
416 * Atomically sets `value' to 16-bit integer.
420 /****f* silcutil/SilcAtomicAPI/silc_atomic_set_int8
425 * void silc_atomic_set_int8(SilcAtomic8 *atomic, SilcUInt8 value);
429 * Atomically sets `value' to 8-bit integer.
433 #define SILC_ATOMIC_SET_INT_F(bits) \
434 static inline void silc_atomic_set_int##bits(SilcAtomic##bits *atomic, \
435 SilcUInt##bits value)
437 #if !defined(SILC_THREADS)
438 #define SILC_ATOMIC_SET_INT(bits, bp, bp2) \
439 SILC_ATOMIC_SET_INT_F(bits) \
441 /* No atomic operations */ \
442 atomic->value = value; \
445 #elif defined(SILC_WIN32)
446 #define SILC_ATOMIC_SET_INT(bits, bp, bp2) \
447 SILC_ATOMIC_SET_INT_F(bits) \
450 InterlockedExchange((LONG)&atomic->value, (LONG)value); \
453 #elif defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64))
454 #define SILC_ATOMIC_SET_INT(bits, bp, bp2) \
455 SILC_ATOMIC_SET_INT_F(bits) \
457 /* GCC + i486 or x86_64 */ \
458 __asm __volatile("xchg" bp " %" bp2 "0, %1" \
460 : "m" (atomic->value), "0" (value)); \
463 #elif defined(__GNUC__) && defined(SILC_IA64)
464 #define SILC_ATOMIC_SET_INT(bits, bp, bp2) \
465 SILC_ATOMIC_SET_INT_F(bits) \
467 /* IA64, memory barrier needed */ \
468 atomic->value = value; \
469 __sync_synchronize(); \
472 #elif defined(__GNUC__) && defined(SILC_POWERPC)
473 #define SILC_ATOMIC_SET_INT(bits, bp, bp2) \
474 SILC_ATOMIC_SET_INT_F(bits) \
476 /* PowerPC, memory barrier needed */ \
477 atomic->value = value; \
478 __asm("sync" : : : "memory"); \
481 #else /* SILC_ATOMIC_MUTEX */
482 #define SILC_ATOMIC_SET_INT(bits, bp, bp2) \
483 SILC_ATOMIC_SET_INT_F(bits) \
486 silc_mutex_lock(atomic->lock); \
487 atomic->value = value; \
488 silc_mutex_unlock(atomic->lock); \
490 #endif /* !SILC_THREADS */
492 SILC_ATOMIC_SET_INT(8, "b", "b")
493 SILC_ATOMIC_SET_INT(16, "w", "w")
494 SILC_ATOMIC_SET_INT(32, "l", "")
496 /****f* silcutil/SilcAtomicAPI/silc_atomic_set_pointer
501 * void silc_atomic_set_pointer(SilcAtomicPointer *atomic, void *pointer);
505 * Atomically sets `pointer' to the atomic variable.
510 void silc_atomic_set_pointer(SilcAtomicPointer *atomic, void *pointer)
512 #if !defined(SILC_THREADS) || \
513 (defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64)))
514 /* No threads, Windows, i486 or x86_64, no memory barrier needed */
515 atomic->value = pointer;
517 #elif defined(SILC_WIN32)
518 InterlockedExchangePointer(&atomic->value, pointer);
520 #elif defined(__GNUC__) && defined(SILC_IA64)
521 /* IA64, memory barrier needed */
522 atomic->value = pointer;
523 __sync_synchronize();
525 #elif defined(__GNUC__) && defined(SILC_POWERPC)
526 /* PowerPC, memory barrier needed */
527 atomic->value = pointer;
528 __asm("sync" : : : "memory");
532 silc_mutex_lock(atomic->lock);
533 atomic->value = pointer;
534 silc_mutex_unlock(atomic->lock);
538 /****f* silcutil/SilcAtomicAPI/silc_atomic_get_int32
543 * SilcUInt32 silc_atomic_get_int32(SilcAtomic32 *atomic);
547 * Returns the current value of the atomic variable.
551 /****f* silcutil/SilcAtomicAPI/silc_atomic_get_int16
556 * SilcUInt32 silc_atomic_get_int16(SilcAtomic16 *atomic);
560 * Returns the current value of the atomic variable.
564 /****f* silcutil/SilcAtomicAPI/silc_atomic_get_int8
569 * SilcUInt32 silc_atomic_get_int8(SilcAtomic8 *atomic);
573 * Returns the current value of the atomic variable.
577 #define SILC_ATOMIC_GET_INT_F(bits) \
579 SilcUInt##bits silc_atomic_get_int##bits(SilcAtomic##bits *atomic)
581 #if !defined(SILC_THREADS) || defined(SILC_WIN32) || \
582 (defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64)))
583 #define SILC_ATOMIC_GET_INT(bits) \
584 SILC_ATOMIC_GET_INT_F(bits) \
586 SilcUInt##bits ret; \
588 /* No threads, Windows, i486 or x86_64, no memory barrier needed */ \
589 ret = atomic->value; \
593 #elif defined(__GNUC__) && defined(SILC_IA64)
594 #define SILC_ATOMIC_GET_INT(bits) \
595 SILC_ATOMIC_GET_INT_F(bits) \
597 SilcUInt##bits ret; \
599 /* IA64, memory barrier needed */ \
600 __sync_synchronize(); \
601 ret = atomic->value; \
605 #elif defined(__GNUC__) && defined(SILC_POWERPC)
606 #define SILC_ATOMIC_GET_INT(bits) \
607 SILC_ATOMIC_GET_INT_F(bits) \
609 SilcUInt##bits ret; \
611 /* PowerPC, memory barrier needed */ \
612 __asm("sync" : : : "memory"); \
613 ret = atomic->value; \
617 #else /* SILC_ATOMIC_MUTEX */
618 #define SILC_ATOMIC_GET_INT(bits) \
619 SILC_ATOMIC_GET_INT_F(bits) \
621 SilcUInt##bits ret; \
624 silc_mutex_lock(atomic->lock); \
625 ret = atomic->value; \
626 silc_mutex_unlock(atomic->lock); \
629 #endif /* !SILC_THREADS */
631 SILC_ATOMIC_GET_INT(8)
632 SILC_ATOMIC_GET_INT(16)
633 SILC_ATOMIC_GET_INT(32)
635 /****f* silcutil/SilcAtomicAPI/silc_atomic_get_pointer
640 * SilcUInt8 silc_atomic_get_pointer(SilcAtomicPointer *atomic)
644 * Returns the current pointer value of the atomic variable.
649 void *silc_atomic_get_pointer(SilcAtomicPointer *atomic)
653 #if !defined(SILC_THREADS) || defined(SILC_WIN32) || \
654 (defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64)))
655 /* No threads, Windows, i486 or x86_64, no memory barrier needed */
656 ret = (void *)atomic->value;
659 #elif defined(__GNUC__) && defined(SILC_IA64)
660 /* IA64, memory barrier needed */
661 __sync_synchronize();
662 ret = (void *)atomic->value;
665 #elif defined(__GNUC__) && defined(SILC_POWERPC)
666 /* PowerPC, memory barrier needed */
667 __asm("sync" : : : "memory");
668 ret = (void *)atomic->value;
673 silc_mutex_lock(atomic->lock);
674 ret = (void *)atomic->value;
675 silc_mutex_unlock(atomic->lock);
680 /****f* silcutil/SilcAtomicAPI/silc_atomic_add_int32
685 * SilcUInt32 silc_atomic_add_int32(SilcAtomic32 *atomic, SilcInt32 value);
689 * Atomically adds `value' to 32-bit integer. Returns the value after
694 /****f* silcutil/SilcAtomicAPI/silc_atomic_add_int16
699 * SilcUInt16 silc_atomic_add_int16(SilcAtomic16 *atomic, SilcInt16 value);
703 * Atomically adds `value' to 16-bit integer. Returns the value after
708 /****f* silcutil/SilcAtomicAPI/silc_atomic_add_int8
713 * SilcUInt8 silc_atomic_add_int8(SilcAtomic8 *atomic, SilcInt8 value);
717 * Atomically adds `value' to 8-bit integer. Returns the value after
722 #define SILC_ATOMIC_ADD_INT_F(bits) \
724 SilcUInt##bits silc_atomic_add_int##bits(SilcAtomic##bits *atomic, \
727 #if !defined(SILC_THREADS)
728 #define SILC_ATOMIC_ADD_INT(bits, bp) \
729 SILC_ATOMIC_ADD_INT_F(bits) \
731 SilcUInt##bits ret; \
732 /* No atomic operations */ \
733 ret = atomic->value; \
734 atomic->value += value; \
735 return ret + value; \
738 #elif defined(SILC_WIN32)
739 #define SILC_ATOMIC_ADD_INT(bits, bp) \
740 SILC_ATOMIC_ADD_INT_F(bits) \
742 SilcUInt##bits ret; \
745 ret = InterlockedExchangeAdd(&atomic->value, val); \
746 return ret + value; \
749 #elif defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64))
750 #define SILC_ATOMIC_ADD_INT(bits, bp) \
751 SILC_ATOMIC_ADD_INT_F(bits) \
753 SilcUInt##bits ret; \
754 /* GCC + i486 or x86_64 */ \
755 __asm __volatile(SILC_SMP_LOCK "xadd" bp " %0, %1" \
756 : "=r" (ret), "+m" (atomic->value) \
758 return ret + value; \
761 #elif defined(__GNUC__) && defined(SILC_IA64)
762 #define SILC_ATOMIC_ADD_INT(bits, bp) \
763 SILC_ATOMIC_ADD_INT_F(bits) \
765 SilcUInt##bits ret; \
766 SilcInt32 val = value; \
767 /* GCC + IA64 (GCC builtin atomic operations) */ \
768 ret = __sync_fetch_and_add(&atomic->value, val); \
769 return ret + value; \
772 #elif defined(__GNUC__) && defined(SILC_POWERPC)
773 #define SILC_ATOMIC_ADD_INT(bits, bp) \
774 SILC_ATOMIC_ADD_INT_F(bits) \
777 SilcInt32 val = value; \
778 /* GCC + PowerPC (code adapted from IBM's documentation) */ \
779 __asm __volatile("0: lwarx %0, 0, %2\n" \
780 " add %0, %1, %0\n" \
781 " stwcx. %0, 0, %2\n" \
784 : "r" (val), "r" (&atomic->value) \
789 #else /* SILC_ATOMIC_MUTEX */
790 #define SILC_ATOMIC_ADD_INT(bits, bp) \
791 SILC_ATOMIC_ADD_INT_F(bits) \
793 SilcUInt##bits ret; \
795 silc_mutex_lock(atomic->lock); \
796 ret = atomic->value; \
797 atomic->value += value; \
798 silc_mutex_unlock(atomic->lock); \
799 return ret + value; \
801 #endif /* !SILC_THREADS */
803 SILC_ATOMIC_ADD_INT(8, "b")
804 SILC_ATOMIC_ADD_INT(16, "w")
805 SILC_ATOMIC_ADD_INT(32, "l")
807 /****f* silcutil/SilcAtomicAPI/silc_atomic_sub_int32
812 * SilcUInt32 silc_atomic_sub_int32(SilcAtomic32 *atomic, SilcInt32 value);
816 * Atomically subtracts `value' from 32-bit integer. Returns the value
821 /****f* silcutil/SilcAtomicAPI/silc_atomic_sub_int16
826 * SilcUInt16 silc_atomic_sub_int16(SilcAtomic16 *atomic, SilcInt16 value);
830 * Atomically subtracts `value' from 16-bit integer. Returns the value
835 /****f* silcutil/SilcAtomicAPI/silc_atomic_sub_int8
840 * SilcUInt8 silc_atomic_sub_int8(SilcAtomic8 *atomic, SilcInt8 value);
844 * Atomically subtracts `value' from 8-bit integer. Returns the value
849 #define silc_atomic_sub_int8(a, v) silc_atomic_add_int8(a, (-v))
850 #define silc_atomic_sub_int16(a, v) silc_atomic_add_int16(a, (-v))
851 #define silc_atomic_sub_int32(a, v) silc_atomic_add_int32(a, (-v))
853 /****f* silcutil/SilcAtomicAPI/silc_atomic_inc32
858 * void silc_atomic_inc32(SilcAtomic32 *atomic);
862 * Atomically increments 32-bit integer by one.
866 /****f* silcutil/SilcAtomicAPI/silc_atomic_inc16
871 * void silc_atomic_inc16(SilcAtomic16 *atomic);
875 * Atomically increments 16-bit integer by one.
879 /****f* silcutil/SilcAtomicAPI/silc_atomic_inc8
884 * void silc_atomic_inc8(SilcAtomic8 *atomic);
888 * Atomically increments 8-bit integer by one.
892 #define SILC_ATOMIC_INC_F(bits) \
893 static inline void silc_atomic_inc##bits(SilcAtomic##bits *atomic)
895 #if !defined(SILC_THREADS)
896 #define SILC_ATOMIC_INC(bits, bp) \
897 SILC_ATOMIC_INC_F(bits) \
899 /* No atomic operations */ \
903 #elif defined(SILC_WIN32)
904 #define SILC_ATOMIC_INC(bits, bp) \
905 SILC_ATOMIC_INC_F(bits) \
908 InterlockedIncrement((LONG)&atomic->value); \
911 #elif defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64))
912 #define SILC_ATOMIC_INC(bits, bp) \
913 SILC_ATOMIC_INC_F(bits) \
915 /* GCC + i486 or x86_64 */ \
916 __asm __volatile(SILC_SMP_LOCK "inc" bp " %0" \
917 : "+m" (atomic->value)); \
920 #elif defined(__GNUC__) && defined(SILC_IA64)
921 #define SILC_ATOMIC_INC(bits, bp) \
922 SILC_ATOMIC_INC_F(bits) \
924 /* GCC + IA64 (GCC builtin atomic operations) */ \
925 __sync_fetch_and_add(&atomic->value, 1); \
928 #elif defined(__GNUC__) && defined(SILC_POWERPC)
929 #define SILC_ATOMIC_INC(bits, bp) \
930 SILC_ATOMIC_INC_F(bits) \
934 /* GCC + PowerPC (code adapted from IBM's documentation) */ \
935 __asm __volatile("0: lwarx %0, 0, %2\n" \
936 " add %0, %1, %0\n" \
937 " stwcx. %0, 0, %2\n" \
940 : "r" (val), "r" (&atomic->value) \
944 #else /* SILC_ATOMIC_MUTEX */
945 #define SILC_ATOMIC_INC(bits, bp) \
946 SILC_ATOMIC_INC_F(bits) \
949 silc_mutex_lock(atomic->lock); \
951 silc_mutex_unlock(atomic->lock); \
953 #endif /* !SILC_THREADS */
955 SILC_ATOMIC_INC(8, "b")
956 SILC_ATOMIC_INC(16, "w")
957 SILC_ATOMIC_INC(32, "l")
959 /****f* silcutil/SilcAtomicAPI/silc_atomic_dec32
964 * void silc_atomic_dec32(SilcAtomic32 *atomic);
968 * Atomically decrements 32-bit integer by one.
972 /****f* silcutil/SilcAtomicAPI/silc_atomic_dec16
977 * void silc_atomic_dec16(SilcAtomic16 *atomic);
981 * Atomically decrements 16-bit integer by one.
985 /****f* silcutil/SilcAtomicAPI/silc_atomic_dec8
990 * void silc_atomic_dec8(SilcAtomic8 *atomic);
994 * Atomically decrements 8-bit integer by one.
998 #define SILC_ATOMIC_DEC_F(bits) \
999 static inline void silc_atomic_dec##bits(SilcAtomic##bits *atomic)
1001 #if !defined(SILC_THREADS)
1002 #define SILC_ATOMIC_DEC(bits, bp) \
1003 SILC_ATOMIC_DEC_F(bits) \
1005 /* No atomic operations */ \
1009 #elif defined(SILC_WIN32)
1010 #define SILC_ATOMIC_DEC(bits, bp) \
1011 SILC_ATOMIC_DEC_F(bits) \
1014 InterlockedDecrement((LONG)&atomic->value); \
1017 #elif defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64))
1018 #define SILC_ATOMIC_DEC(bits, bp) \
1019 SILC_ATOMIC_DEC_F(bits) \
1021 /* GCC + i486 or x86_64 */ \
1022 __asm __volatile(SILC_SMP_LOCK "dec" bp " %0" \
1023 : "+m" (atomic->value)); \
1026 #elif defined(__GNUC__) && defined(SILC_IA64)
1027 #define SILC_ATOMIC_DEC(bits, bp) \
1028 SILC_ATOMIC_DEC_F(bits) \
1030 /* GCC + IA64 (GCC builtin atomic operations) */ \
1031 __sync_fetch_and_sub(&atomic->value, 1); \
1034 #elif defined(__GNUC__) && defined(SILC_POWERPC)
1035 #define SILC_ATOMIC_DEC(bits, bp) \
1036 SILC_ATOMIC_DEC_F(bits) \
1039 SilcInt32 val = -1; \
1040 /* GCC + PowerPC (code adapted from IBM's documentation) */ \
1041 __asm __volatile("0: lwarx %0, 0, %2\n" \
1042 " add %0, %1, %0\n" \
1043 " stwcx. %0, 0, %2\n" \
1046 : "r" (val), "r" (&atomic->value) \
1050 #else /* SILC_ATOMIC_MUTEX */
1051 #define SILC_ATOMIC_DEC(bits, bp) \
1052 SILC_ATOMIC_DEC_F(bits) \
1055 silc_mutex_lock(atomic->lock); \
1057 silc_mutex_unlock(atomic->lock); \
1059 #endif /* !SILC_THREADS */
1061 SILC_ATOMIC_DEC(8, "b")
1062 SILC_ATOMIC_DEC(16, "w")
1063 SILC_ATOMIC_DEC(32, "l")
1065 /****f* silcutil/SilcAtomicAPI/silc_atomic_cas32
1070 * SilcBool silc_atomic_cas32(SilcAtomic32 *atomic, SilcUInt32 old_val,
1071 * SilcUInt32 new_val)
1075 * Performs compare and swap (CAS). Atomically compares if the variable
1076 * `atomic' has the value `old_val' and in that case swaps it with the
1077 * value `new_val'. Returns TRUE if the old value was same and it was
1078 * swapped and FALSE if it differed and was not swapped.
1082 /****f* silcutil/SilcAtomicAPI/silc_atomic_cas16
1087 * SilcBool silc_atomic_cas16(SilcAtomic16 *atomic, SilcUInt16 old_val,
1088 * SilcUInt16 new_val)
1092 * Performs compare and swap (CAS). Atomically compares if the variable
1093 * `atomic' has the value `old_val' and in that case swaps it with the
1094 * value `new_val'. Returns TRUE if the old value was same and it was
1095 * swapped and FALSE if it differed and was not swapped.
1099 /****f* silcutil/SilcAtomicAPI/silc_atomic_cas8
1104 * SilcBool silc_atomic_cas8(SilcAtomic8 *atomic, SilcUInt8 old_val,
1105 * SilcUInt8 new_val)
1109 * Performs compare and swap (CAS). Atomically compares if the variable
1110 * `atomic' has the value `old_val' and in that case swaps it with the
1111 * value `new_val'. Returns TRUE if the old value was same and it was
1112 * swapped and FALSE if it differed and was not swapped.
1116 #define SILC_ATOMIC_CAS_F(bits) \
1117 static inline SilcBool silc_atomic_cas##bits(SilcAtomic##bits *atomic, \
1118 SilcInt##bits old_val, \
1119 SilcInt##bits new_val)
1121 #if !defined(SILC_THREADS)
1122 #define SILC_ATOMIC_CAS(bits, bp) \
1123 SILC_ATOMIC_CAS_F(bits) \
1125 /* No atomic operations */ \
1126 if (atomic->value == (SilcUInt##bits)old_val) { \
1127 atomic->value = new_val; \
1133 #elif defined(SILC_WIN32)
1134 #define SILC_ATOMIC_CAS(bits, bp) \
1135 SILC_ATOMIC_CAS_F(bits) \
1138 LONG o = old_val, n = new_val; \
1139 return InterlockedCompareExchange(&atomic->value, n, o) == o; \
1142 #elif defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64))
1143 #define SILC_ATOMIC_CAS(bits, bp) \
1144 SILC_ATOMIC_CAS_F(bits) \
1146 /* GCC + i486 or x86_64 */ \
1147 SilcUInt##bits ret; \
1148 __asm __volatile(SILC_SMP_LOCK "cmpxchg" bp " %2, %1" \
1149 : "=a" (ret), "=m" (atomic->value) \
1150 : "r" (new_val), "m" (atomic->value), "0" (old_val)); \
1151 return ret == (SilcUInt##bits)old_val; \
1154 #elif defined(__GNUC__) && defined(SILC_IA64)
1155 #define SILC_ATOMIC_CAS(bits, bp) \
1156 SILC_ATOMIC_CAS_F(bits) \
1158 /* GCC + IA64 (GCC builtin atomic operations) */ \
1159 SilcUInt32 o = old_val, n = new_val; \
1160 return __sync_bool_compare_and_swap(&atomic->value, o, n); \
1163 #elif defined(__GNUC__) && defined(SILC_POWERPC)
1164 #define SILC_ATOMIC_CAS(bits, bp) \
1165 SILC_ATOMIC_CAS_F(bits) \
1167 /* GCC + PowerPC */ \
1171 #else /* SILC_ATOMIC_MUTEX */
1172 #define SILC_ATOMIC_CAS(bits, bp) \
1173 SILC_ATOMIC_CAS_F(bits) \
1176 silc_mutex_lock(atomic->lock); \
1177 if (atomic->value == (SilcUInt##bits)old_val) { \
1178 atomic->value = new_val; \
1179 silc_mutex_unlock(atomic->lock); \
1182 silc_mutex_unlock(atomic->lock); \
1185 #endif /* !SILC_THREADS */
1187 SILC_ATOMIC_CAS(8, "b")
1188 SILC_ATOMIC_CAS(16, "w")
1189 SILC_ATOMIC_CAS(32, "l")
1191 /****f* silcutil/SilcAtomicAPI/silc_atomic_cas_pointer
1196 * SilcBool silc_atomic_cas_pointer(SilcAtomicPointer *atomic,
1197 * void *old_ptr, void *new_ptr);
1201 * Performs compare and swap (CAS). Atomically compares if the variable
1202 * `atomic' has the pointer `old_ptr' and in that case swaps it with the
1203 * pointer `new_ptr'. Returns TRUE if the old pointer was same and it was
1204 * swapped and FALSE if it differed and was not swapped.
1209 SilcBool silc_atomic_cas_pointer(SilcAtomicPointer *atomic, void *old_val,
1212 #if !defined(SILC_THREADS)
1213 /* No atomic operations */
1214 if (atomic->value == old_val) {
1215 atomic->value = new_val;
1220 #elif defined(SILC_WIN32)
1222 return InterlockedCompareExchangePointer(&atomic->value, new_val, old_val)
1225 #elif defined(__GNUC__) && defined(SILC_I486)
1228 __asm __volatile(SILC_SMP_LOCK "cmpxchgl %2, %1"
1229 : "=a" (ret), "=m" (atomic->value)
1230 : "c" (new_val), "m" (atomic->value), "0" (old_val));
1231 return ret == old_val;
1233 #elif defined(__GNUC__) && defined(SILC_X86_64)
1236 __asm __volatile(SILC_SMP_LOCK "cmpxchgq %q2, %1"
1237 : "=a" (ret), "=m" (atomic->value)
1238 : "c" (new_val), "m" (atomic->value), "0" (old_val));
1239 return ret == old_val;
1241 #elif defined(__GNUC__) && defined(SILC_IA64)
1242 /* GCC + IA64 (GCC builtin atomic operations) */
1243 return __sync_bool_compare_and_swap((long *)&atomic->value, (long)old_val,
1246 #elif defined(__GNUC__) && defined(SILC_POWERPC)
1252 silc_mutex_lock(atomic->lock);
1253 if (atomic->value == old_val) {
1254 atomic->value = new_val;
1255 silc_mutex_unlock(atomic->lock);
1258 silc_mutex_unlock(atomic->lock);
1263 #endif /* SILCATOMIC_H */