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
43 /* For now we always assume SMP */
46 /* Use lock prefix only on true SMP systems */
48 #define SILC_SMP_LOCK "lock; "
53 /****s* silcutil/SilcAtomicAPI/SilcAtomic32
57 * typedef struct { ... } SilcAtomic32;
61 * The atomic operation structure given as argument to all atomic
62 * operation functions. It hols the actual 32-bit atomic variable.
66 * SilcAtomic32 refcnt;
68 * // Initialize atomic variable
69 * silc_atomic_init32(&refcnt, 0);
72 * // Increment referene counter
73 * silc_atomic_add_int32(&refcnt, 1);
76 * // Uninitialize atomic variable
77 * silc_atomic_uninit32(&refcnt);
81 /****s* silcutil/SilcAtomicAPI/SilcAtomic16
85 * typedef struct { ... } SilcAtomic16;
89 * The atomic operation structure given as argument to all atomic
90 * operation functions. It hols the actual 16-bit atomic variable.
94 * SilcAtomic16 refcnt;
96 * // Initialize atomic variable
97 * silc_atomic_init16(&refcnt, 0);
100 * // Increment referene counter
101 * silc_atomic_add_int16(&refcnt, 1);
104 * // Uninitialize atomic variable
105 * silc_atomic_uninit16(&refcnt);
109 /****s* silcutil/SilcAtomicAPI/SilcAtomic8
113 * typedef struct { ... } SilcAtomic8;
117 * The atomic operation structure given as argument to all atomic
118 * operation functions. It hols the actual 8-bit atomic variable.
122 * SilcAtomic8 refcnt;
124 * // Initialize atomic variable
125 * silc_atomic_init8(&refcnt, 0);
128 * // Increment referene counter
129 * silc_atomic_add_int8(&refcnt, 1);
132 * // Uninitialize atomic variable
133 * silc_atomic_uninit8(&refcnt);
137 /****s* silcutil/SilcAtomicAPI/SilcAtomicPointer
141 * typedef struct { ... } SilcAtomicPointer;
145 * The atomic operation structure given as argument to all atomic
146 * operation functions. It hols the actual pointer variable.
150 * SilcAtomicPointer ptr;
152 * // Initialize atomic variable
153 * silc_atomic_init_pointer(&ptr, NULL);
157 * silc_atomic_set_pointer(&ptr, context);
160 * // Uninitialize atomic variable
161 * silc_atomic_uninit_pointer(&ptr);
165 #if !defined(SILC_THREADS) || defined(SILC_WIN32) || (defined(__GNUC__) && \
166 (defined(SILC_I486) || defined(SILC_X86_64) || defined(SILC_IA64) || \
167 defined(SILC_POWERPC)))
169 volatile SilcUInt32 value;
172 volatile void *value;
175 #define SILC_ATOMIC_MUTEX
178 volatile SilcUInt32 value;
182 volatile void *value;
186 #if !defined(SILC_THREADS) || (defined(__GNUC__) && (defined(SILC_I486) || \
187 defined(SILC_X86_64)))
189 volatile SilcUInt16 value;
191 #elif defined(SILC_WIN32) || (defined(__GNUC__) && (defined(SILC_IA64) || \
192 defined(SILC_POWERPC)))
194 volatile SilcUInt32 value;
199 volatile SilcUInt16 value;
203 #if !defined(SILC_THREADS) || (defined(__GNUC__) && (defined(SILC_I486) || \
204 defined(SILC_X86_64)))
206 volatile SilcUInt8 value;
208 #elif defined(SILC_WIN32) || (defined(__GNUC__) && (defined(SILC_IA64) || \
209 defined(SILC_POWERPC)))
211 volatile SilcUInt32 value;
216 volatile SilcUInt8 value;
220 /****f* silcutil/SilcAtomicAPI/silc_atomic_init32
225 * SilcBool silc_atomic_init32(SilcAtomic32 *atomic, SilcUInt32 value);
229 * Initializes the atomic variable `atomic', and sets the `value' as its
230 * inital value. Returns FALSE on error. To uninitialize call the
231 * silc_atomic_uninit32 function.
235 /****f* silcutil/SilcAtomicAPI/silc_atomic_init16
240 * SilcBool silc_atomic_init16(SilcAtomic16 *atomic, SilcUInt16 value);
244 * Initializes the atomic variable `atomic', and sets the `value' as its
245 * inital value. Returns FALSE on error. To uninitialize call the
246 * silc_atomic_uninit32 function.
250 /****f* silcutil/SilcAtomicAPI/silc_atomic_init8
255 * SilcBool silc_atomic_init8(SilcAtomic8 *atomic, SilcUInt8 value);
259 * Initializes the atomic variable `atomic', and sets the `value' as its
260 * inital value. Returns FALSE on error. To uninitialize call the
261 * silc_atomic_uninit8 function.
265 /****f* silcutil/SilcAtomicAPI/silc_atomic_init_pointer
270 * SilcBool silc_atomic_init_pointer(SilcAtomicPointer *atomic,
275 * Initializes the atomic pointer variable `atomic', and sets the `pointer'
276 * as its inital pointer. Returns FALSE on error. To uninitialize call
277 * the silc_atomic_uninit_pointer function.
281 #define SILC_ATOMIC_INIT_F(name, bits, type) \
283 SilcBool silc_atomic_init##name(SilcAtomic##bits *atomic, type value)
285 #if defined(SILC_ATOMIC_MUTEX)
286 #define SILC_ATOMIC_INIT(name, bits, type) \
287 SILC_ATOMIC_INIT_F(name, bits, type) \
289 atomic->value = value; \
290 return silc_mutex_alloc(&atomic->lock); \
293 #define SILC_ATOMIC_INIT(name, bits, type) \
294 SILC_ATOMIC_INIT_F(name, bits, type) \
296 atomic->value = value; \
299 #endif /* SILC_ATOMIC_MUTEX */
301 SILC_ATOMIC_INIT(8, 8, SilcUInt8)
302 SILC_ATOMIC_INIT(16, 16, SilcUInt16)
303 SILC_ATOMIC_INIT(32, 32, SilcUInt32)
304 SILC_ATOMIC_INIT(_pointer, Pointer, void *)
306 /****f* silcutil/SilcAtomicAPI/silc_atomic_uninit32
311 * void silc_atomic_uninit32(SilcAtomic32 *atomic);
315 * Uninitializes the atomic variable `atomic'. This should alwyas be
316 * called after the atomic variable is not used anymore.
320 /****f* silcutil/SilcAtomicAPI/silc_atomic_uninit16
325 * void silc_atomic_uninit16(SilcAtomic16 *atomic);
329 * Uninitializes the atomic variable `atomic'. This should alwyas be
330 * called after the atomic variable is not used anymore.
334 /****f* silcutil/SilcAtomicAPI/silc_atomic_uninit8
339 * void silc_atomic_uninit8(SilcAtomic8 *atomic);
343 * Uninitializes the atomic variable `atomic'. This should alwyas be
344 * called after the atomic variable is not used anymore.
348 /****f* silcutil/SilcAtomicAPI/silc_atomic_uninit_pointer
353 * void silc_atomic_uninit_pointer(SilcAtomicPointer *atomic);
357 * Uninitializes the atomic variable `atomic'. This should alwyas be
358 * called after the atomic variable is not used anymore.
362 #define SILC_ATOMIC_UNINIT_F(bits, t) \
363 static inline void silc_atomic_uninit##bits(SilcAtomic##t *atomic)
365 #if defined(SILC_ATOMIC_MUTEX)
366 #define SILC_ATOMIC_UNINIT(bits, t) \
367 SILC_ATOMIC_UNINIT_F(bits, t) \
369 silc_mutex_free(atomic->lock); \
372 #define SILC_ATOMIC_UNINIT(bits, t) \
373 SILC_ATOMIC_UNINIT_F(bits, t) \
375 memset(atomic, 0, sizeof(*atomic)); \
377 #endif /* SILC_ATOMIC_MUTEX */
379 SILC_ATOMIC_UNINIT(8, 8)
380 SILC_ATOMIC_UNINIT(16, 16)
381 SILC_ATOMIC_UNINIT(32, 32)
382 SILC_ATOMIC_UNINIT(_pointer, Pointer)
384 /****f* silcutil/SilcAtomicAPI/silc_atomic_set_int32
389 * void silc_atomic_set_int32(SilcAtomic32 *atomic, SilcUInt32 value);
393 * Atomically sets `value' to 32-bit integer.
397 /****f* silcutil/SilcAtomicAPI/silc_atomic_set_int16
402 * void silc_atomic_set_int16(SilcAtomic16 *atomic, SilcUInt16 value);
406 * Atomically sets `value' to 16-bit integer.
410 /****f* silcutil/SilcAtomicAPI/silc_atomic_set_int8
415 * void silc_atomic_set_int8(SilcAtomic8 *atomic, SilcUInt8 value);
419 * Atomically sets `value' to 8-bit integer.
423 #define SILC_ATOMIC_SET_INT_F(bits) \
424 static inline void silc_atomic_set_int##bits(SilcAtomic##bits *atomic, \
425 SilcUInt##bits value)
427 #if !defined(SILC_THREADS)
428 #define SILC_ATOMIC_SET_INT(bits, bp, bp2) \
429 SILC_ATOMIC_SET_INT_F(bits) \
431 /* No atomic operations */ \
432 atomic->value = value; \
435 #elif defined(SILC_WIN32)
436 #define SILC_ATOMIC_SET_INT(bits, bp, bp2) \
437 SILC_ATOMIC_SET_INT_F(bits) \
440 InterlockedExchange((LONG)&atomic->value, (LONG)value); \
443 #elif defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64))
444 #define SILC_ATOMIC_SET_INT(bits, bp, bp2) \
445 SILC_ATOMIC_SET_INT_F(bits) \
447 /* GCC + i486 or x86_64 */ \
448 __asm __volatile("xchg" bp " %" bp2 "0, %1" \
450 : "m" (atomic->value), "0" (value)); \
453 #elif defined(__GNUC__) && defined(SILC_IA64)
454 #define SILC_ATOMIC_SET_INT(bits, bp, bp2) \
455 SILC_ATOMIC_SET_INT_F(bits) \
457 /* IA64, memory barrier needed */ \
458 atomic->value = value; \
459 __sync_synchronize(); \
462 #elif defined(__GNUC__) && defined(SILC_POWERPC)
463 #define SILC_ATOMIC_SET_INT(bits, bp, bp2) \
464 SILC_ATOMIC_SET_INT_F(bits) \
466 /* PowerPC, memory barrier needed */ \
467 atomic->value = value; \
468 __asm("sync" : : : "memory"); \
471 #else /* SILC_ATOMIC_MUTEX */
472 #define SILC_ATOMIC_SET_INT(bits, bp, bp2) \
473 SILC_ATOMIC_SET_INT_F(bits) \
476 silc_mutex_lock(atomic->lock); \
477 atomic->value = value; \
478 silc_mutex_unlock(atomic->lock); \
480 #endif /* !SILC_THREADS */
482 SILC_ATOMIC_SET_INT(8, "b", "b")
483 SILC_ATOMIC_SET_INT(16, "w", "w")
484 SILC_ATOMIC_SET_INT(32, "l", "")
486 /****f* silcutil/SilcAtomicAPI/silc_atomic_set_pointer
491 * void silc_atomic_set_pointer(SilcAtomicPointer *atomic, void *pointer);
495 * Atomically sets `pointer' to the atomic variable.
500 void silc_atomic_set_pointer(SilcAtomicPointer *atomic, void *pointer)
502 #if !defined(SILC_THREADS) || \
503 (defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64)))
504 /* No threads, Windows, i486 or x86_64, no memory barrier needed */
505 atomic->value = pointer;
507 #elif defined(SILC_WIN32)
508 InterlockedExchangePointer(&atomic->value, pointer);
510 #elif defined(__GNUC__) && defined(SILC_IA64)
511 /* IA64, memory barrier needed */
512 atomic->value = pointer;
513 __sync_synchronize();
515 #elif defined(__GNUC__) && defined(SILC_POWERPC)
516 /* PowerPC, memory barrier needed */
517 atomic->value = pointer;
518 __asm("sync" : : : "memory");
522 silc_mutex_lock(atomic->lock);
523 atomic->value = pointer;
524 silc_mutex_unlock(atomic->lock);
528 /****f* silcutil/SilcAtomicAPI/silc_atomic_get_int32
533 * SilcUInt32 silc_atomic_get_int32(SilcAtomic32 *atomic);
537 * Returns the current value of the atomic variable.
541 /****f* silcutil/SilcAtomicAPI/silc_atomic_get_int16
546 * SilcUInt32 silc_atomic_get_int16(SilcAtomic16 *atomic);
550 * Returns the current value of the atomic variable.
554 /****f* silcutil/SilcAtomicAPI/silc_atomic_get_int8
559 * SilcUInt32 silc_atomic_get_int8(SilcAtomic8 *atomic);
563 * Returns the current value of the atomic variable.
567 #define SILC_ATOMIC_GET_INT_F(bits) \
569 SilcUInt##bits silc_atomic_get_int##bits(SilcAtomic##bits *atomic)
571 #if !defined(SILC_THREADS) || defined(SILC_WIN32) || \
572 (defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64)))
573 #define SILC_ATOMIC_GET_INT(bits) \
574 SILC_ATOMIC_GET_INT_F(bits) \
576 SilcUInt##bits ret; \
578 /* No threads, Windows, i486 or x86_64, no memory barrier needed */ \
579 ret = atomic->value; \
583 #elif defined(__GNUC__) && defined(SILC_IA64)
584 #define SILC_ATOMIC_GET_INT(bits) \
585 SILC_ATOMIC_GET_INT_F(bits) \
587 SilcUInt##bits ret; \
589 /* IA64, memory barrier needed */ \
590 __sync_synchronize(); \
591 ret = atomic->value; \
595 #elif defined(__GNUC__) && defined(SILC_POWERPC)
596 #define SILC_ATOMIC_GET_INT(bits) \
597 SILC_ATOMIC_GET_INT_F(bits) \
599 SilcUInt##bits ret; \
601 /* PowerPC, memory barrier needed */ \
602 __asm("sync" : : : "memory"); \
603 ret = atomic->value; \
607 #else /* SILC_ATOMIC_MUTEX */
608 #define SILC_ATOMIC_GET_INT(bits) \
609 SILC_ATOMIC_GET_INT_F(bits) \
611 SilcUInt##bits ret; \
614 silc_mutex_lock(atomic->lock); \
615 ret = atomic->value; \
616 silc_mutex_unlock(atomic->lock); \
619 #endif /* !SILC_THREADS */
621 SILC_ATOMIC_GET_INT(8)
622 SILC_ATOMIC_GET_INT(16)
623 SILC_ATOMIC_GET_INT(32)
625 /****f* silcutil/SilcAtomicAPI/silc_atomic_get_pointer
630 * SilcUInt8 silc_atomic_get_pointer(SilcAtomicPointer *atomic)
634 * Returns the current pointer value of the atomic variable.
639 void *silc_atomic_get_pointer(SilcAtomicPointer *atomic)
643 #if !defined(SILC_THREADS) || defined(SILC_WIN32) || \
644 (defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64)))
645 /* No threads, Windows, i486 or x86_64, no memory barrier needed */
646 ret = (void *)atomic->value;
649 #elif defined(__GNUC__) && defined(SILC_IA64)
650 /* IA64, memory barrier needed */
651 __sync_synchronize();
652 ret = (void *)atomic->value;
655 #elif defined(__GNUC__) && defined(SILC_POWERPC)
656 /* PowerPC, memory barrier needed */
657 __asm("sync" : : : "memory");
658 ret = (void *)atomic->value;
663 silc_mutex_lock(atomic->lock);
664 ret = (void *)atomic->value;
665 silc_mutex_unlock(atomic->lock);
670 /****f* silcutil/SilcAtomicAPI/silc_atomic_add_int32
675 * SilcUInt32 silc_atomic_add_int32(SilcAtomic32 *atomic, SilcInt32 value);
679 * Atomically adds `value' to 32-bit integer. Returns the value after
684 /****f* silcutil/SilcAtomicAPI/silc_atomic_add_int16
689 * SilcUInt16 silc_atomic_add_int16(SilcAtomic16 *atomic, SilcInt16 value);
693 * Atomically adds `value' to 16-bit integer. Returns the value after
698 /****f* silcutil/SilcAtomicAPI/silc_atomic_add_int8
703 * SilcUInt8 silc_atomic_add_int8(SilcAtomic8 *atomic, SilcInt8 value);
707 * Atomically adds `value' to 8-bit integer. Returns the value after
712 #define SILC_ATOMIC_ADD_INT_F(bits) \
714 SilcUInt##bits silc_atomic_add_int##bits(SilcAtomic##bits *atomic, \
717 #if !defined(SILC_THREADS)
718 #define SILC_ATOMIC_ADD_INT(bits, bp) \
719 SILC_ATOMIC_ADD_INT_F(bits) \
721 SilcUInt##bits ret; \
722 /* No atomic operations */ \
723 ret = atomic->value; \
724 atomic->value += value; \
725 return ret + value; \
728 #elif defined(SILC_WIN32)
729 #define SILC_ATOMIC_ADD_INT(bits, bp) \
730 SILC_ATOMIC_ADD_INT_F(bits) \
732 SilcUInt##bits ret; \
735 ret = InterlockedExchangeAdd(&atomic->value, val); \
736 return ret + value; \
739 #elif defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64))
740 #define SILC_ATOMIC_ADD_INT(bits, bp) \
741 SILC_ATOMIC_ADD_INT_F(bits) \
743 SilcUInt##bits ret; \
744 /* GCC + i486 or x86_64 */ \
745 __asm __volatile(SILC_SMP_LOCK "xadd" bp " %0, %1" \
746 : "=r" (ret), "+m" (atomic->value) \
748 return ret + value; \
751 #elif defined(__GNUC__) && defined(SILC_IA64)
752 #define SILC_ATOMIC_ADD_INT(bits, bp) \
753 SILC_ATOMIC_ADD_INT_F(bits) \
755 SilcUInt##bits ret; \
756 SilcInt32 val = value; \
757 /* GCC + IA64 (GCC builtin atomic operations) */ \
758 ret = __sync_fetch_and_add(&atomic->value, val); \
759 return ret + value; \
762 #elif defined(__GNUC__) && defined(SILC_POWERPC)
763 #define SILC_ATOMIC_ADD_INT(bits, bp) \
764 SILC_ATOMIC_ADD_INT_F(bits) \
767 SilcInt32 val = value; \
768 /* GCC + PowerPC (code adapted from IBM's documentation) */ \
769 __asm __volatile("0: lwarx %0, 0, %2\n" \
770 " add %0, %1, %0\n" \
771 " stwcx. %0, 0, %2\n" \
774 : "r" (val), "r" (&atomic->value) \
779 #else /* SILC_ATOMIC_MUTEX */
780 #define SILC_ATOMIC_ADD_INT(bits, bp) \
781 SILC_ATOMIC_ADD_INT_F(bits) \
783 SilcUInt##bits ret; \
785 silc_mutex_lock(atomic->lock); \
786 ret = atomic->value; \
787 atomic->value += value; \
788 silc_mutex_unlock(atomic->lock); \
789 return ret + value; \
791 #endif /* !SILC_THREADS */
793 SILC_ATOMIC_ADD_INT(8, "b")
794 SILC_ATOMIC_ADD_INT(16, "w")
795 SILC_ATOMIC_ADD_INT(32, "l")
797 /****f* silcutil/SilcAtomicAPI/silc_atomic_sub_int32
802 * SilcUInt32 silc_atomic_sub_int32(SilcAtomic32 *atomic, SilcInt32 value);
806 * Atomically subtracts `value' from 32-bit integer. Returns the value
811 /****f* silcutil/SilcAtomicAPI/silc_atomic_sub_int16
816 * SilcUInt16 silc_atomic_sub_int16(SilcAtomic16 *atomic, SilcInt16 value);
820 * Atomically subtracts `value' from 16-bit integer. Returns the value
825 /****f* silcutil/SilcAtomicAPI/silc_atomic_sub_int8
830 * SilcUInt8 silc_atomic_sub_int8(SilcAtomic8 *atomic, SilcInt8 value);
834 * Atomically subtracts `value' from 8-bit integer. Returns the value
839 #define silc_atomic_sub_int8(a, v) silc_atomic_add_int8(a, (-v))
840 #define silc_atomic_sub_int16(a, v) silc_atomic_add_int16(a, (-v))
841 #define silc_atomic_sub_int32(a, v) silc_atomic_add_int32(a, (-v))
843 /****f* silcutil/SilcAtomicAPI/silc_atomic_inc32
848 * void silc_atomic_inc32(SilcAtomic32 *atomic);
852 * Atomically increments 32-bit integer by one.
856 /****f* silcutil/SilcAtomicAPI/silc_atomic_inc16
861 * void silc_atomic_inc16(SilcAtomic16 *atomic);
865 * Atomically increments 16-bit integer by one.
869 /****f* silcutil/SilcAtomicAPI/silc_atomic_inc8
874 * void silc_atomic_inc8(SilcAtomic8 *atomic);
878 * Atomically increments 8-bit integer by one.
882 #define SILC_ATOMIC_INC_F(bits) \
883 static inline void silc_atomic_inc##bits(SilcAtomic##bits *atomic)
885 #if !defined(SILC_THREADS)
886 #define SILC_ATOMIC_INC(bits, bp) \
887 SILC_ATOMIC_INC_F(bits) \
889 /* No atomic operations */ \
893 #elif defined(SILC_WIN32)
894 #define SILC_ATOMIC_INC(bits, bp) \
895 SILC_ATOMIC_INC_F(bits) \
898 InterlockedIncrement((LONG)&atomic->value); \
901 #elif defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64))
902 #define SILC_ATOMIC_INC(bits, bp) \
903 SILC_ATOMIC_INC_F(bits) \
905 /* GCC + i486 or x86_64 */ \
906 __asm __volatile(SILC_SMP_LOCK "inc" bp " %0" \
907 : "+m" (atomic->value)); \
910 #elif defined(__GNUC__) && defined(SILC_IA64)
911 #define SILC_ATOMIC_INC(bits, bp) \
912 SILC_ATOMIC_INC_F(bits) \
914 /* GCC + IA64 (GCC builtin atomic operations) */ \
915 __sync_fetch_and_add(&atomic->value, 1); \
918 #elif defined(__GNUC__) && defined(SILC_POWERPC)
919 #define SILC_ATOMIC_INC(bits, bp) \
920 SILC_ATOMIC_INC_F(bits) \
924 /* GCC + PowerPC (code adapted from IBM's documentation) */ \
925 __asm __volatile("0: lwarx %0, 0, %2\n" \
926 " add %0, %1, %0\n" \
927 " stwcx. %0, 0, %2\n" \
930 : "r" (val), "r" (&atomic->value) \
934 #else /* SILC_ATOMIC_MUTEX */
935 #define SILC_ATOMIC_INC(bits, bp) \
936 SILC_ATOMIC_INC_F(bits) \
939 silc_mutex_lock(atomic->lock); \
941 silc_mutex_unlock(atomic->lock); \
943 #endif /* !SILC_THREADS */
945 SILC_ATOMIC_INC(8, "b")
946 SILC_ATOMIC_INC(16, "w")
947 SILC_ATOMIC_INC(32, "l")
949 /****f* silcutil/SilcAtomicAPI/silc_atomic_dec32
954 * void silc_atomic_dec32(SilcAtomic32 *atomic);
958 * Atomically decrements 32-bit integer by one.
962 /****f* silcutil/SilcAtomicAPI/silc_atomic_dec16
967 * void silc_atomic_dec16(SilcAtomic16 *atomic);
971 * Atomically decrements 16-bit integer by one.
975 /****f* silcutil/SilcAtomicAPI/silc_atomic_dec8
980 * void silc_atomic_dec8(SilcAtomic8 *atomic);
984 * Atomically decrements 8-bit integer by one.
988 #define SILC_ATOMIC_DEC_F(bits) \
989 static inline void silc_atomic_dec##bits(SilcAtomic##bits *atomic)
991 #if !defined(SILC_THREADS)
992 #define SILC_ATOMIC_DEC(bits, bp) \
993 SILC_ATOMIC_DEC_F(bits) \
995 /* No atomic operations */ \
999 #elif defined(SILC_WIN32)
1000 #define SILC_ATOMIC_DEC(bits, bp) \
1001 SILC_ATOMIC_DEC_F(bits) \
1004 InterlockedDecrement((LONG)&atomic->value); \
1007 #elif defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64))
1008 #define SILC_ATOMIC_DEC(bits, bp) \
1009 SILC_ATOMIC_DEC_F(bits) \
1011 /* GCC + i486 or x86_64 */ \
1012 __asm __volatile(SILC_SMP_LOCK "dec" bp " %0" \
1013 : "+m" (atomic->value)); \
1016 #elif defined(__GNUC__) && defined(SILC_IA64)
1017 #define SILC_ATOMIC_DEC(bits, bp) \
1018 SILC_ATOMIC_DEC_F(bits) \
1020 /* GCC + IA64 (GCC builtin atomic operations) */ \
1021 __sync_fetch_and_sub(&atomic->value, 1); \
1024 #elif defined(__GNUC__) && defined(SILC_POWERPC)
1025 #define SILC_ATOMIC_DEC(bits, bp) \
1026 SILC_ATOMIC_DEC_F(bits) \
1029 SilcInt32 val = -1; \
1030 /* GCC + PowerPC (code adapted from IBM's documentation) */ \
1031 __asm __volatile("0: lwarx %0, 0, %2\n" \
1032 " add %0, %1, %0\n" \
1033 " stwcx. %0, 0, %2\n" \
1036 : "r" (val), "r" (&atomic->value) \
1040 #else /* SILC_ATOMIC_MUTEX */
1041 #define SILC_ATOMIC_DEC(bits, bp) \
1042 SILC_ATOMIC_DEC_F(bits) \
1045 silc_mutex_lock(atomic->lock); \
1047 silc_mutex_unlock(atomic->lock); \
1049 #endif /* !SILC_THREADS */
1051 SILC_ATOMIC_DEC(8, "b")
1052 SILC_ATOMIC_DEC(16, "w")
1053 SILC_ATOMIC_DEC(32, "l")
1055 /****f* silcutil/SilcAtomicAPI/silc_atomic_cas32
1060 * SilcBool silc_atomic_cas32(SilcAtomic32 *atomic, SilcUInt32 old_val,
1061 * SilcUInt32 new_val)
1065 * Performs compare and swap (CAS). Atomically compares if the variable
1066 * `atomic' has the value `old_val' and in that case swaps it with the
1067 * value `new_val'. Returns TRUE if the old value was same and it was
1068 * swapped and FALSE if it differed and was not swapped.
1072 /****f* silcutil/SilcAtomicAPI/silc_atomic_cas16
1077 * SilcBool silc_atomic_cas16(SilcAtomic16 *atomic, SilcUInt16 old_val,
1078 * SilcUInt16 new_val)
1082 * Performs compare and swap (CAS). Atomically compares if the variable
1083 * `atomic' has the value `old_val' and in that case swaps it with the
1084 * value `new_val'. Returns TRUE if the old value was same and it was
1085 * swapped and FALSE if it differed and was not swapped.
1089 /****f* silcutil/SilcAtomicAPI/silc_atomic_cas8
1094 * SilcBool silc_atomic_cas8(SilcAtomic8 *atomic, SilcUInt8 old_val,
1095 * SilcUInt8 new_val)
1099 * Performs compare and swap (CAS). Atomically compares if the variable
1100 * `atomic' has the value `old_val' and in that case swaps it with the
1101 * value `new_val'. Returns TRUE if the old value was same and it was
1102 * swapped and FALSE if it differed and was not swapped.
1106 #define SILC_ATOMIC_CAS_F(bits) \
1107 static inline SilcBool silc_atomic_cas##bits(SilcAtomic##bits *atomic, \
1108 SilcInt##bits old_val, \
1109 SilcInt##bits new_val)
1111 #if !defined(SILC_THREADS)
1112 #define SILC_ATOMIC_CAS(bits, bp) \
1113 SILC_ATOMIC_CAS_F(bits) \
1115 /* No atomic operations */ \
1116 if (atomic->value == (SilcUInt##bits)old_val) { \
1117 atomic->value = new_val; \
1123 #elif defined(SILC_WIN32)
1124 #define SILC_ATOMIC_CAS(bits, bp) \
1125 SILC_ATOMIC_CAS_F(bits) \
1128 LONG o = old_val, n = new_val; \
1129 return InterlockedCompareExchange(&atomic->value, n, o) == o; \
1132 #elif defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64))
1133 #define SILC_ATOMIC_CAS(bits, bp) \
1134 SILC_ATOMIC_CAS_F(bits) \
1136 /* GCC + i486 or x86_64 */ \
1137 SilcUInt##bits ret; \
1138 __asm __volatile(SILC_SMP_LOCK "cmpxchg" bp " %2, %1" \
1139 : "=a" (ret), "=m" (atomic->value) \
1140 : "r" (new_val), "m" (atomic->value), "0" (old_val)); \
1141 return ret == (SilcUInt##bits)old_val; \
1144 #elif defined(__GNUC__) && defined(SILC_IA64)
1145 #define SILC_ATOMIC_CAS(bits, bp) \
1146 SILC_ATOMIC_CAS_F(bits) \
1148 /* GCC + IA64 (GCC builtin atomic operations) */ \
1149 SilcUInt32 o = old_val, n = new_val; \
1150 return __sync_bool_compare_and_swap(&atomic->value, o, n); \
1153 #elif defined(__GNUC__) && defined(SILC_POWERPC)
1154 #define SILC_ATOMIC_CAS(bits, bp) \
1155 SILC_ATOMIC_CAS_F(bits) \
1157 /* GCC + PowerPC */ \
1161 #else /* SILC_ATOMIC_MUTEX */
1162 #define SILC_ATOMIC_CAS(bits, bp) \
1163 SILC_ATOMIC_CAS_F(bits) \
1166 silc_mutex_lock(atomic->lock); \
1167 if (atomic->value == (SilcUInt##bits)old_val) { \
1168 atomic->value = new_val; \
1169 silc_mutex_unlock(atomic->lock); \
1172 silc_mutex_unlock(atomic->lock); \
1175 #endif /* !SILC_THREADS */
1177 SILC_ATOMIC_CAS(8, "b")
1178 SILC_ATOMIC_CAS(16, "w")
1179 SILC_ATOMIC_CAS(32, "l")
1181 /****f* silcutil/SilcAtomicAPI/silc_atomic_cas_pointer
1186 * SilcBool silc_atomic_cas_pointer(SilcAtomicPointer *atomic,
1187 * void *old_ptr, void *new_ptr);
1191 * Performs compare and swap (CAS). Atomically compares if the variable
1192 * `atomic' has the pointer `old_ptr' and in that case swaps it with the
1193 * pointer `new_ptr'. Returns TRUE if the old pointer was same and it was
1194 * swapped and FALSE if it differed and was not swapped.
1199 SilcBool silc_atomic_cas_pointer(SilcAtomicPointer *atomic, void *old_val,
1202 #if !defined(SILC_THREADS)
1203 /* No atomic operations */
1204 if (atomic->value == old_val) {
1205 atomic->value = new_val;
1210 #elif defined(SILC_WIN32)
1212 return InterlockedCompareExchangePointer(&atomic->value, new_val, old_val)
1215 #elif defined(__GNUC__) && defined(SILC_I486)
1218 __asm __volatile(SILC_SMP_LOCK "cmpxchgl %2, %1"
1219 : "=a" (ret), "=m" (atomic->value)
1220 : "c" (new_val), "m" (atomic->value), "0" (old_val));
1221 return ret == old_val;
1223 #elif defined(__GNUC__) && defined(SILC_X86_64)
1226 __asm __volatile(SILC_SMP_LOCK "cmpxchgq %q2, %1"
1227 : "=a" (ret), "=m" (atomic->value)
1228 : "c" (new_val), "m" (atomic->value), "0" (old_val));
1229 return ret == old_val;
1231 #elif defined(__GNUC__) && defined(SILC_IA64)
1232 /* GCC + IA64 (GCC builtin atomic operations) */
1233 return __sync_bool_compare_and_swap((long *)&atomic->value, (long)old_val,
1236 #elif defined(__GNUC__) && defined(SILC_POWERPC)
1242 silc_mutex_lock(atomic->lock);
1243 if (atomic->value == old_val) {
1244 atomic->value = new_val;
1245 silc_mutex_unlock(atomic->lock);
1248 silc_mutex_unlock(atomic->lock);
1253 #endif /* SILCATOMIC_H */