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 /* For now we always assume SMP */
59 /* Use lock prefix only on true SMP systems */
61 #define SILC_SMP_LOCK "lock; "
66 /****s* silcutil/SilcAtomicAPI/SilcAtomic32
70 * typedef struct { ... } SilcAtomic32;
74 * The atomic operation structure given as argument to all atomic
75 * operation functions. It hols the actual 32-bit atomic variable.
79 * SilcAtomic32 refcnt;
81 * // Initialize atomic variable
82 * silc_atomic_init32(&refcnt, 0);
85 * // Increment referene counter
86 * silc_atomic_add_int32(&refcnt, 1);
89 * // Uninitialize atomic variable
90 * silc_atomic_uninit32(&refcnt);
94 /****s* silcutil/SilcAtomicAPI/SilcAtomic16
98 * typedef struct { ... } SilcAtomic16;
102 * The atomic operation structure given as argument to all atomic
103 * operation functions. It hols the actual 16-bit atomic variable.
107 * SilcAtomic16 refcnt;
109 * // Initialize atomic variable
110 * silc_atomic_init16(&refcnt, 0);
113 * // Increment referene counter
114 * silc_atomic_add_int16(&refcnt, 1);
117 * // Uninitialize atomic variable
118 * silc_atomic_uninit16(&refcnt);
122 /****s* silcutil/SilcAtomicAPI/SilcAtomic8
126 * typedef struct { ... } SilcAtomic8;
130 * The atomic operation structure given as argument to all atomic
131 * operation functions. It hols the actual 8-bit atomic variable.
135 * SilcAtomic8 refcnt;
137 * // Initialize atomic variable
138 * silc_atomic_init8(&refcnt, 0);
141 * // Increment referene counter
142 * silc_atomic_add_int8(&refcnt, 1);
145 * // Uninitialize atomic variable
146 * silc_atomic_uninit8(&refcnt);
150 /****s* silcutil/SilcAtomicAPI/SilcAtomicPointer
154 * typedef struct { ... } SilcAtomicPointer;
158 * The atomic operation structure given as argument to all atomic
159 * operation functions. It hols the actual pointer variable.
163 * SilcAtomicPointer ptr;
165 * // Initialize atomic variable
166 * silc_atomic_init_pointer(&ptr, NULL);
170 * silc_atomic_set_pointer(&ptr, context);
173 * // Uninitialize atomic variable
174 * silc_atomic_uninit_pointer(&ptr);
178 #if !defined(SILC_THREADS) || defined(SILC_WIN32) || (defined(__GNUC__) && \
179 (defined(SILC_I486) || defined(SILC_X86_64) || defined(SILC_IA64) || \
180 defined(SILC_POWERPC)))
182 volatile SilcUInt32 value;
185 volatile void *value;
188 #define SILC_ATOMIC_MUTEX
191 volatile SilcUInt32 value;
195 volatile void *value;
199 #if !defined(SILC_THREADS) || (defined(__GNUC__) && (defined(SILC_I486) || \
200 defined(SILC_X86_64)))
202 volatile SilcUInt16 value;
204 #elif defined(SILC_WIN32) || (defined(__GNUC__) && (defined(SILC_IA64) || \
205 defined(SILC_POWERPC)))
207 volatile SilcUInt32 value;
212 volatile SilcUInt16 value;
216 #if !defined(SILC_THREADS) || (defined(__GNUC__) && (defined(SILC_I486) || \
217 defined(SILC_X86_64)))
219 volatile SilcUInt8 value;
221 #elif defined(SILC_WIN32) || (defined(__GNUC__) && (defined(SILC_IA64) || \
222 defined(SILC_POWERPC)))
224 volatile SilcUInt32 value;
229 volatile SilcUInt8 value;
233 /****f* silcutil/SilcAtomicAPI/silc_atomic_init32
238 * SilcBool silc_atomic_init32(SilcAtomic32 *atomic, SilcUInt32 value);
242 * Initializes the atomic variable `atomic', and sets the `value' as its
243 * inital value. Returns FALSE on error. To uninitialize call the
244 * silc_atomic_uninit32 function.
248 /****f* silcutil/SilcAtomicAPI/silc_atomic_init16
253 * SilcBool silc_atomic_init16(SilcAtomic16 *atomic, SilcUInt16 value);
257 * Initializes the atomic variable `atomic', and sets the `value' as its
258 * inital value. Returns FALSE on error. To uninitialize call the
259 * silc_atomic_uninit32 function.
263 /****f* silcutil/SilcAtomicAPI/silc_atomic_init8
268 * SilcBool silc_atomic_init8(SilcAtomic8 *atomic, SilcUInt8 value);
272 * Initializes the atomic variable `atomic', and sets the `value' as its
273 * inital value. Returns FALSE on error. To uninitialize call the
274 * silc_atomic_uninit8 function.
278 /****f* silcutil/SilcAtomicAPI/silc_atomic_init_pointer
283 * SilcBool silc_atomic_init_pointer(SilcAtomicPointer *atomic,
288 * Initializes the atomic pointer variable `atomic', and sets the `pointer'
289 * as its inital pointer. Returns FALSE on error. To uninitialize call
290 * the silc_atomic_uninit_pointer function.
294 #define SILC_ATOMIC_INIT_F(name, bits, type) \
296 SilcBool silc_atomic_init##name(SilcAtomic##bits *atomic, type value)
298 #if defined(SILC_ATOMIC_MUTEX)
299 #define SILC_ATOMIC_INIT(name, bits, type) \
300 SILC_ATOMIC_INIT_F(name, bits, type) \
302 atomic->value = value; \
303 return silc_mutex_alloc(&atomic->lock); \
306 #define SILC_ATOMIC_INIT(name, bits, type) \
307 SILC_ATOMIC_INIT_F(name, bits, type) \
309 atomic->value = value; \
312 #endif /* SILC_ATOMIC_MUTEX */
314 SILC_ATOMIC_INIT(8, 8, SilcUInt8)
315 SILC_ATOMIC_INIT(16, 16, SilcUInt16)
316 SILC_ATOMIC_INIT(32, 32, SilcUInt32)
317 SILC_ATOMIC_INIT(_pointer, Pointer, void *)
319 /****f* silcutil/SilcAtomicAPI/silc_atomic_uninit32
324 * void silc_atomic_uninit32(SilcAtomic32 *atomic);
328 * Uninitializes the atomic variable `atomic'. This should alwyas be
329 * called after the atomic variable is not used anymore.
333 /****f* silcutil/SilcAtomicAPI/silc_atomic_uninit16
338 * void silc_atomic_uninit16(SilcAtomic16 *atomic);
342 * Uninitializes the atomic variable `atomic'. This should alwyas be
343 * called after the atomic variable is not used anymore.
347 /****f* silcutil/SilcAtomicAPI/silc_atomic_uninit8
352 * void silc_atomic_uninit8(SilcAtomic8 *atomic);
356 * Uninitializes the atomic variable `atomic'. This should alwyas be
357 * called after the atomic variable is not used anymore.
361 /****f* silcutil/SilcAtomicAPI/silc_atomic_uninit_pointer
366 * void silc_atomic_uninit_pointer(SilcAtomicPointer *atomic);
370 * Uninitializes the atomic variable `atomic'. This should alwyas be
371 * called after the atomic variable is not used anymore.
375 #define SILC_ATOMIC_UNINIT_F(bits, t) \
376 static inline void silc_atomic_uninit##bits(SilcAtomic##t *atomic)
378 #if defined(SILC_ATOMIC_MUTEX)
379 #define SILC_ATOMIC_UNINIT(bits, t) \
380 SILC_ATOMIC_UNINIT_F(bits, t) \
382 silc_mutex_free(atomic->lock); \
385 #define SILC_ATOMIC_UNINIT(bits, t) \
386 SILC_ATOMIC_UNINIT_F(bits, t) \
388 memset(atomic, 0, sizeof(*atomic)); \
390 #endif /* SILC_ATOMIC_MUTEX */
392 SILC_ATOMIC_UNINIT(8, 8)
393 SILC_ATOMIC_UNINIT(16, 16)
394 SILC_ATOMIC_UNINIT(32, 32)
395 SILC_ATOMIC_UNINIT(_pointer, Pointer)
397 /****f* silcutil/SilcAtomicAPI/silc_atomic_set_int32
402 * void silc_atomic_set_int32(SilcAtomic32 *atomic, SilcUInt32 value);
406 * Atomically sets `value' to 32-bit integer.
410 /****f* silcutil/SilcAtomicAPI/silc_atomic_set_int16
415 * void silc_atomic_set_int16(SilcAtomic16 *atomic, SilcUInt16 value);
419 * Atomically sets `value' to 16-bit integer.
423 /****f* silcutil/SilcAtomicAPI/silc_atomic_set_int8
428 * void silc_atomic_set_int8(SilcAtomic8 *atomic, SilcUInt8 value);
432 * Atomically sets `value' to 8-bit integer.
436 #define SILC_ATOMIC_SET_INT_F(bits) \
437 static inline void silc_atomic_set_int##bits(SilcAtomic##bits *atomic, \
438 SilcUInt##bits value)
440 #if !defined(SILC_THREADS)
441 #define SILC_ATOMIC_SET_INT(bits, bp, bp2) \
442 SILC_ATOMIC_SET_INT_F(bits) \
444 /* No atomic operations */ \
445 atomic->value = value; \
448 #elif defined(SILC_WIN32)
449 #define SILC_ATOMIC_SET_INT(bits, bp, bp2) \
450 SILC_ATOMIC_SET_INT_F(bits) \
453 InterlockedExchange((LONG)&atomic->value, (LONG)value); \
456 #elif defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64))
457 #define SILC_ATOMIC_SET_INT(bits, bp, bp2) \
458 SILC_ATOMIC_SET_INT_F(bits) \
460 /* GCC + i486 or x86_64 */ \
461 __asm __volatile("xchg" bp " %" bp2 "0, %1" \
463 : "m" (atomic->value), "0" (value)); \
466 #elif defined(__GNUC__) && defined(SILC_IA64)
467 #define SILC_ATOMIC_SET_INT(bits, bp, bp2) \
468 SILC_ATOMIC_SET_INT_F(bits) \
470 /* IA64, memory barrier needed */ \
471 atomic->value = value; \
472 __sync_synchronize(); \
475 #elif defined(__GNUC__) && defined(SILC_POWERPC)
476 #define SILC_ATOMIC_SET_INT(bits, bp, bp2) \
477 SILC_ATOMIC_SET_INT_F(bits) \
479 /* PowerPC, memory barrier needed */ \
480 atomic->value = value; \
481 __asm("sync" : : : "memory"); \
484 #else /* SILC_ATOMIC_MUTEX */
485 #define SILC_ATOMIC_SET_INT(bits, bp, bp2) \
486 SILC_ATOMIC_SET_INT_F(bits) \
489 silc_mutex_lock(atomic->lock); \
490 atomic->value = value; \
491 silc_mutex_unlock(atomic->lock); \
493 #endif /* !SILC_THREADS */
495 SILC_ATOMIC_SET_INT(8, "b", "b")
496 SILC_ATOMIC_SET_INT(16, "w", "w")
497 SILC_ATOMIC_SET_INT(32, "l", "")
499 /****f* silcutil/SilcAtomicAPI/silc_atomic_set_pointer
504 * void silc_atomic_set_pointer(SilcAtomicPointer *atomic, void *pointer);
508 * Atomically sets `pointer' to the atomic variable.
513 void silc_atomic_set_pointer(SilcAtomicPointer *atomic, void *pointer)
515 #if !defined(SILC_THREADS) || \
516 (defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64)))
517 /* No threads, Windows, i486 or x86_64, no memory barrier needed */
518 atomic->value = pointer;
520 #elif defined(SILC_WIN32)
521 InterlockedExchangePointer(&atomic->value, pointer);
523 #elif defined(__GNUC__) && defined(SILC_IA64)
524 /* IA64, memory barrier needed */
525 atomic->value = pointer;
526 __sync_synchronize();
528 #elif defined(__GNUC__) && defined(SILC_POWERPC)
529 /* PowerPC, memory barrier needed */
530 atomic->value = pointer;
531 __asm("sync" : : : "memory");
535 silc_mutex_lock(atomic->lock);
536 atomic->value = pointer;
537 silc_mutex_unlock(atomic->lock);
541 /****f* silcutil/SilcAtomicAPI/silc_atomic_get_int32
546 * SilcUInt32 silc_atomic_get_int32(SilcAtomic32 *atomic);
550 * Returns the current value of the atomic variable.
554 /****f* silcutil/SilcAtomicAPI/silc_atomic_get_int16
559 * SilcUInt32 silc_atomic_get_int16(SilcAtomic16 *atomic);
563 * Returns the current value of the atomic variable.
567 /****f* silcutil/SilcAtomicAPI/silc_atomic_get_int8
572 * SilcUInt32 silc_atomic_get_int8(SilcAtomic8 *atomic);
576 * Returns the current value of the atomic variable.
580 #define SILC_ATOMIC_GET_INT_F(bits) \
582 SilcUInt##bits silc_atomic_get_int##bits(SilcAtomic##bits *atomic)
584 #if !defined(SILC_THREADS) || defined(SILC_WIN32) || \
585 (defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64)))
586 #define SILC_ATOMIC_GET_INT(bits) \
587 SILC_ATOMIC_GET_INT_F(bits) \
589 SilcUInt##bits ret; \
591 /* No threads, Windows, i486 or x86_64, no memory barrier needed */ \
592 ret = atomic->value; \
596 #elif defined(__GNUC__) && defined(SILC_IA64)
597 #define SILC_ATOMIC_GET_INT(bits) \
598 SILC_ATOMIC_GET_INT_F(bits) \
600 SilcUInt##bits ret; \
602 /* IA64, memory barrier needed */ \
603 __sync_synchronize(); \
604 ret = atomic->value; \
608 #elif defined(__GNUC__) && defined(SILC_POWERPC)
609 #define SILC_ATOMIC_GET_INT(bits) \
610 SILC_ATOMIC_GET_INT_F(bits) \
612 SilcUInt##bits ret; \
614 /* PowerPC, memory barrier needed */ \
615 __asm("sync" : : : "memory"); \
616 ret = atomic->value; \
620 #else /* SILC_ATOMIC_MUTEX */
621 #define SILC_ATOMIC_GET_INT(bits) \
622 SILC_ATOMIC_GET_INT_F(bits) \
624 SilcUInt##bits ret; \
627 silc_mutex_lock(atomic->lock); \
628 ret = atomic->value; \
629 silc_mutex_unlock(atomic->lock); \
632 #endif /* !SILC_THREADS */
634 SILC_ATOMIC_GET_INT(8)
635 SILC_ATOMIC_GET_INT(16)
636 SILC_ATOMIC_GET_INT(32)
638 /****f* silcutil/SilcAtomicAPI/silc_atomic_get_pointer
643 * SilcUInt8 silc_atomic_get_pointer(SilcAtomicPointer *atomic)
647 * Returns the current pointer value of the atomic variable.
652 void *silc_atomic_get_pointer(SilcAtomicPointer *atomic)
656 #if !defined(SILC_THREADS) || defined(SILC_WIN32) || \
657 (defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64)))
658 /* No threads, Windows, i486 or x86_64, no memory barrier needed */
659 ret = (void *)atomic->value;
662 #elif defined(__GNUC__) && defined(SILC_IA64)
663 /* IA64, memory barrier needed */
664 __sync_synchronize();
665 ret = (void *)atomic->value;
668 #elif defined(__GNUC__) && defined(SILC_POWERPC)
669 /* PowerPC, memory barrier needed */
670 __asm("sync" : : : "memory");
671 ret = (void *)atomic->value;
676 silc_mutex_lock(atomic->lock);
677 ret = (void *)atomic->value;
678 silc_mutex_unlock(atomic->lock);
683 /****f* silcutil/SilcAtomicAPI/silc_atomic_add_int32
688 * SilcUInt32 silc_atomic_add_int32(SilcAtomic32 *atomic, SilcInt32 value);
692 * Atomically adds `value' to 32-bit integer. Returns the value after
697 /****f* silcutil/SilcAtomicAPI/silc_atomic_add_int16
702 * SilcUInt16 silc_atomic_add_int16(SilcAtomic16 *atomic, SilcInt16 value);
706 * Atomically adds `value' to 16-bit integer. Returns the value after
711 /****f* silcutil/SilcAtomicAPI/silc_atomic_add_int8
716 * SilcUInt8 silc_atomic_add_int8(SilcAtomic8 *atomic, SilcInt8 value);
720 * Atomically adds `value' to 8-bit integer. Returns the value after
725 #define SILC_ATOMIC_ADD_INT_F(bits) \
727 SilcUInt##bits silc_atomic_add_int##bits(SilcAtomic##bits *atomic, \
730 #if !defined(SILC_THREADS)
731 #define SILC_ATOMIC_ADD_INT(bits, bp) \
732 SILC_ATOMIC_ADD_INT_F(bits) \
734 SilcUInt##bits ret; \
735 /* No atomic operations */ \
736 ret = atomic->value; \
737 atomic->value += value; \
738 return ret + value; \
741 #elif defined(SILC_WIN32)
742 #define SILC_ATOMIC_ADD_INT(bits, bp) \
743 SILC_ATOMIC_ADD_INT_F(bits) \
745 SilcUInt##bits ret; \
748 ret = InterlockedExchangeAdd(&atomic->value, val); \
749 return ret + value; \
752 #elif defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64))
753 #define SILC_ATOMIC_ADD_INT(bits, bp) \
754 SILC_ATOMIC_ADD_INT_F(bits) \
756 SilcUInt##bits ret; \
757 /* GCC + i486 or x86_64 */ \
758 __asm __volatile(SILC_SMP_LOCK "xadd" bp " %0, %1" \
759 : "=r" (ret), "+m" (atomic->value) \
761 return ret + value; \
764 #elif defined(__GNUC__) && defined(SILC_IA64)
765 #define SILC_ATOMIC_ADD_INT(bits, bp) \
766 SILC_ATOMIC_ADD_INT_F(bits) \
768 SilcUInt##bits ret; \
769 SilcInt32 val = value;
770 /* GCC + IA64 (GCC builtin atomic operations) */ \
771 ret = __sync_fetch_and_add(&atomic->value, val); \
772 return ret + value; \
775 #elif defined(__GNUC__) && defined(SILC_POWERPC)
776 #define SILC_ATOMIC_ADD_INT(bits, bp) \
777 SILC_ATOMIC_ADD_INT_F(bits) \
780 SilcInt32 val = value; \
781 /* GCC + PowerPC (code adapted from IBM's documentation) */ \
782 __asm __volatile("0: lwarx %0, 0, %2\n" \
783 " add %0, %1, %0\n" \
784 " stwcx. %0, 0, %2\n" \
787 : "r" (val), "r" (&atomic->value) \
792 #else /* SILC_ATOMIC_MUTEX */
793 #define SILC_ATOMIC_ADD_INT(bits, bp) \
794 SILC_ATOMIC_ADD_INT_F(bits) \
796 SilcUInt##bits ret; \
798 silc_mutex_lock(atomic->lock); \
799 ret = atomic->value; \
800 atomic->value += value; \
801 silc_mutex_unlock(atomic->lock); \
802 return ret + value; \
804 #endif /* !SILC_THREADS */
806 SILC_ATOMIC_ADD_INT(8, "b")
807 SILC_ATOMIC_ADD_INT(16, "w")
808 SILC_ATOMIC_ADD_INT(32, "l")
810 /****f* silcutil/SilcAtomicAPI/silc_atomic_sub_int32
815 * SilcUInt32 silc_atomic_sub_int32(SilcAtomic32 *atomic, SilcInt32 value);
819 * Atomically subtracts `value' from 32-bit integer. Returns the value
824 /****f* silcutil/SilcAtomicAPI/silc_atomic_sub_int16
829 * SilcUInt16 silc_atomic_sub_int16(SilcAtomic16 *atomic, SilcInt16 value);
833 * Atomically subtracts `value' from 16-bit integer. Returns the value
838 /****f* silcutil/SilcAtomicAPI/silc_atomic_sub_int8
843 * SilcUInt8 silc_atomic_sub_int8(SilcAtomic8 *atomic, SilcInt8 value);
847 * Atomically subtracts `value' from 8-bit integer. Returns the value
852 #define silc_atomic_sub_int8(a, v) silc_atomic_add_int8(a, (-v))
853 #define silc_atomic_sub_int16(a, v) silc_atomic_add_int16(a, (-v))
854 #define silc_atomic_sub_int32(a, v) silc_atomic_add_int32(a, (-v))
856 /****f* silcutil/SilcAtomicAPI/silc_atomic_inc32
861 * void silc_atomic_inc32(SilcAtomic32 *atomic);
865 * Atomically increments 32-bit integer by one.
869 /****f* silcutil/SilcAtomicAPI/silc_atomic_inc16
874 * void silc_atomic_inc16(SilcAtomic16 *atomic);
878 * Atomically increments 16-bit integer by one.
882 /****f* silcutil/SilcAtomicAPI/silc_atomic_inc8
887 * void silc_atomic_inc8(SilcAtomic8 *atomic);
891 * Atomically increments 8-bit integer by one.
895 #define SILC_ATOMIC_INC_F(bits) \
896 static inline void silc_atomic_inc##bits(SilcAtomic##bits *atomic)
898 #if !defined(SILC_THREADS)
899 #define SILC_ATOMIC_INC(bits, bp) \
900 SILC_ATOMIC_INC_F(bits) \
902 /* No atomic operations */ \
906 #elif defined(SILC_WIN32)
907 #define SILC_ATOMIC_INC(bits, bp) \
908 SILC_ATOMIC_INC_F(bits) \
911 InterlockedIncrement((LONG)&atomic->value); \
914 #elif defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64))
915 #define SILC_ATOMIC_INC(bits, bp) \
916 SILC_ATOMIC_INC_F(bits) \
918 /* GCC + i486 or x86_64 */ \
919 __asm __volatile(SILC_SMP_LOCK "inc" bp " %0" \
920 : "+m" (atomic->value)); \
923 #elif defined(__GNUC__) && defined(SILC_IA64)
924 #define SILC_ATOMIC_INC(bits, bp) \
925 SILC_ATOMIC_INC_F(bits) \
927 /* GCC + IA64 (GCC builtin atomic operations) */ \
928 __sync_fetch_and_add(&atomic->value, 1); \
931 #elif defined(__GNUC__) && defined(SILC_POWERPC)
932 #define SILC_ATOMIC_INC(bits, bp) \
933 SILC_ATOMIC_INC_F(bits) \
937 /* GCC + PowerPC (code adapted from IBM's documentation) */ \
938 __asm __volatile("0: lwarx %0, 0, %2\n" \
939 " add %0, %1, %0\n" \
940 " stwcx. %0, 0, %2\n" \
943 : "r" (val), "r" (&atomic->value) \
947 #else /* SILC_ATOMIC_MUTEX */
948 #define SILC_ATOMIC_INC(bits, bp) \
949 SILC_ATOMIC_INC_F(bits) \
952 silc_mutex_lock(atomic->lock); \
954 silc_mutex_unlock(atomic->lock); \
956 #endif /* !SILC_THREADS */
958 SILC_ATOMIC_INC(8, "b")
959 SILC_ATOMIC_INC(16, "w")
960 SILC_ATOMIC_INC(32, "l")
962 /****f* silcutil/SilcAtomicAPI/silc_atomic_dec32
967 * void silc_atomic_dec32(SilcAtomic32 *atomic);
971 * Atomically decrements 32-bit integer by one.
975 /****f* silcutil/SilcAtomicAPI/silc_atomic_dec16
980 * void silc_atomic_dec16(SilcAtomic16 *atomic);
984 * Atomically decrements 16-bit integer by one.
988 /****f* silcutil/SilcAtomicAPI/silc_atomic_dec8
993 * void silc_atomic_dec8(SilcAtomic8 *atomic);
997 * Atomically decrements 8-bit integer by one.
1001 #define SILC_ATOMIC_DEC_F(bits) \
1002 static inline void silc_atomic_dec##bits(SilcAtomic##bits *atomic)
1004 #if !defined(SILC_THREADS)
1005 #define SILC_ATOMIC_DEC(bits, bp) \
1006 SILC_ATOMIC_DEC_F(bits) \
1008 /* No atomic operations */ \
1012 #elif defined(SILC_WIN32)
1013 #define SILC_ATOMIC_DEC(bits, bp) \
1014 SILC_ATOMIC_DEC_F(bits) \
1017 InterlockedDecrement((LONG)&atomic->value); \
1020 #elif defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64))
1021 #define SILC_ATOMIC_DEC(bits, bp) \
1022 SILC_ATOMIC_DEC_F(bits) \
1024 /* GCC + i486 or x86_64 */ \
1025 __asm __volatile(SILC_SMP_LOCK "dec" bp " %0" \
1026 : "+m" (atomic->value)); \
1029 #elif defined(__GNUC__) && defined(SILC_IA64)
1030 #define SILC_ATOMIC_DEC(bits, bp) \
1031 SILC_ATOMIC_DEC_F(bits) \
1033 /* GCC + IA64 (GCC builtin atomic operations) */ \
1034 __sync_fetch_and_sub(&atomic->value, 1); \
1037 #elif defined(__GNUC__) && defined(SILC_POWERPC)
1038 #define SILC_ATOMIC_DEC(bits, bp) \
1039 SILC_ATOMIC_DEC_F(bits) \
1042 SilcInt32 val = -1; \
1043 /* GCC + PowerPC (code adapted from IBM's documentation) */ \
1044 __asm __volatile("0: lwarx %0, 0, %2\n" \
1045 " add %0, %1, %0\n" \
1046 " stwcx. %0, 0, %2\n" \
1049 : "r" (val), "r" (&atomic->value) \
1053 #else /* SILC_ATOMIC_MUTEX */
1054 #define SILC_ATOMIC_DEC(bits, bp) \
1055 SILC_ATOMIC_DEC_F(bits) \
1058 silc_mutex_lock(atomic->lock); \
1060 silc_mutex_unlock(atomic->lock); \
1062 #endif /* !SILC_THREADS */
1064 SILC_ATOMIC_DEC(8, "b")
1065 SILC_ATOMIC_DEC(16, "w")
1066 SILC_ATOMIC_DEC(32, "l")
1068 /****f* silcutil/SilcAtomicAPI/silc_atomic_cas32
1073 * SilcBool silc_atomic_cas32(SilcAtomic32 *atomic, SilcUInt32 old_val,
1074 * SilcUInt32 new_val)
1078 * Performs compare and swap (CAS). Atomically compares if the variable
1079 * `atomic' has the value `old_val' and in that case swaps it with the
1080 * value `new_val'. Returns TRUE if the old value was same and it was
1081 * swapped and FALSE if it differed and was not swapped.
1085 /****f* silcutil/SilcAtomicAPI/silc_atomic_cas16
1090 * SilcBool silc_atomic_cas16(SilcAtomic16 *atomic, SilcUInt16 old_val,
1091 * SilcUInt16 new_val)
1095 * Performs compare and swap (CAS). Atomically compares if the variable
1096 * `atomic' has the value `old_val' and in that case swaps it with the
1097 * value `new_val'. Returns TRUE if the old value was same and it was
1098 * swapped and FALSE if it differed and was not swapped.
1102 /****f* silcutil/SilcAtomicAPI/silc_atomic_cas8
1107 * SilcBool silc_atomic_cas8(SilcAtomic8 *atomic, SilcUInt8 old_val,
1108 * SilcUInt8 new_val)
1112 * Performs compare and swap (CAS). Atomically compares if the variable
1113 * `atomic' has the value `old_val' and in that case swaps it with the
1114 * value `new_val'. Returns TRUE if the old value was same and it was
1115 * swapped and FALSE if it differed and was not swapped.
1119 #define SILC_ATOMIC_CAS_F(bits) \
1120 static inline SilcBool silc_atomic_cas##bits(SilcAtomic##bits *atomic, \
1121 SilcInt##bits old_val, \
1122 SilcInt##bits new_val)
1124 #if !defined(SILC_THREADS)
1125 #define SILC_ATOMIC_CAS(bits, bp) \
1126 SILC_ATOMIC_CAS_F(bits) \
1128 /* No atomic operations */ \
1129 if (atomic->value == (SilcUInt##bits)old_val) { \
1130 atomic->value = new_val; \
1136 #elif defined(SILC_WIN32)
1137 #define SILC_ATOMIC_CAS(bits, bp) \
1138 SILC_ATOMIC_CAS_F(bits) \
1141 LONG o = old_val, n = new_val; \
1142 return InterlockedCompareExchange(&atomic->value, n, o) == o; \
1145 #elif defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64))
1146 #define SILC_ATOMIC_CAS(bits, bp) \
1147 SILC_ATOMIC_CAS_F(bits) \
1149 /* GCC + i486 or x86_64 */ \
1150 SilcUInt##bits ret; \
1151 __asm __volatile(SILC_SMP_LOCK "cmpxchg" bp " %2, %1" \
1152 : "=a" (ret), "=m" (atomic->value) \
1153 : "r" (new_val), "m" (atomic->value), "0" (old_val)); \
1154 return ret == (SilcUInt##bits)old_val; \
1157 #elif defined(__GNUC__) && defined(SILC_IA64)
1158 #define SILC_ATOMIC_CAS(bits, bp) \
1159 SILC_ATOMIC_CAS_F(bits) \
1161 /* GCC + IA64 (GCC builtin atomic operations) */ \
1162 SilcUInt32 o = old_val, n = new_val; \
1163 return __sync_bool_compare_and_swap(&atomic->value, o, n); \
1166 #elif defined(__GNUC__) && defined(SILC_POWERPC)
1167 #define SILC_ATOMIC_CAS(bits, bp) \
1168 SILC_ATOMIC_CAS_F(bits) \
1170 /* GCC + PowerPC */ \
1174 #else /* SILC_ATOMIC_MUTEX */
1175 #define SILC_ATOMIC_CAS(bits, bp) \
1176 SILC_ATOMIC_CAS_F(bits) \
1179 silc_mutex_lock(atomic->lock); \
1180 if (atomic->value == (SilcUInt##bits)old_val) { \
1181 atomic->value = new_val; \
1182 silc_mutex_unlock(atomic->lock); \
1185 silc_mutex_unlock(atomic->lock); \
1188 #endif /* !SILC_THREADS */
1190 SILC_ATOMIC_CAS(8, "b")
1191 SILC_ATOMIC_CAS(16, "w")
1192 SILC_ATOMIC_CAS(32, "l")
1194 /****f* silcutil/SilcAtomicAPI/silc_atomic_cas_pointer
1199 * SilcBool silc_atomic_cas_pointer(SilcAtomicPointer *atomic,
1200 * void *old_ptr, void *new_ptr);
1204 * Performs compare and swap (CAS). Atomically compares if the variable
1205 * `atomic' has the pointer `old_ptr' and in that case swaps it with the
1206 * pointer `new_ptr'. Returns TRUE if the old pointer was same and it was
1207 * swapped and FALSE if it differed and was not swapped.
1212 SilcBool silc_atomic_cas_pointer(SilcAtomicPointer *atomic, void *old_val,
1215 #if !defined(SILC_THREADS)
1216 /* No atomic operations */
1217 if (atomic->value == old_val) {
1218 atomic->value = new_val;
1223 #elif defined(SILC_WIN32)
1225 return InterlockedCompareExchangePointer(&atomic->value, new_val, old_val)
1228 #elif defined(__GNUC__) && defined(SILC_I486)
1231 __asm __volatile(SILC_SMP_LOCK "cmpxchgl %2, %1"
1232 : "=a" (ret), "=m" (atomic->value)
1233 : "c" (new_val), "m" (atomic->value), "0" (old_val));
1234 return ret == old_val;
1236 #elif defined(__GNUC__) && defined(SILC_X86_64)
1239 __asm __volatile(SILC_SMP_LOCK "cmpxchgq %q2, %1"
1240 : "=a" (ret), "=m" (atomic->value)
1241 : "c" (new_val), "m" (atomic->value), "0" (old_val));
1242 return ret == old_val;
1244 #elif defined(__GNUC__) && defined(SILC_IA64)
1245 /* GCC + IA64 (GCC builtin atomic operations) */
1246 return __sync_bool_compare_and_swap((long)&atomic->value, (long)old_val,
1249 #elif defined(__GNUC__) && defined(SILC_POWERPC)
1255 silc_mutex_lock(atomic->lock);
1256 if (atomic->value == old_val) {
1257 atomic->value = new_val;
1258 silc_mutex_unlock(atomic->lock);
1261 silc_mutex_unlock(atomic->lock);
1266 #endif /* SILCATOMIC_H */