8, 16 and 32-bit atomic integers, 32 and 64 bit atomic pointers.
[silc.git] / lib / silcutil / silcatomic.h
1 /*
2
3   silcatomic.h
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2006 Pekka Riikonen
8
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.
12
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.
17
18 */
19
20 /****h* silcutil/SILC Atomic Operations Interface
21  *
22  * DESCRIPTION
23  *
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.
30  *
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
34  * integers.
35  *
36  * Fast operations are supported on: x86, x86_64, ia64, PPC
37  *
38  ***/
39
40 #ifndef SILCATOMIC_H
41 #define SILCATOMIC_H
42
43 /****s* silcutil/SilcAtomicAPI/SilcAtomic32
44  *
45  * NAME
46  *
47  *    typedef struct { ... } SilcAtomic32;
48  *
49  * DESCRIPTION
50  *
51  *    The atomic operation structure given as argument to all atomic
52  *    operation functions.  It hols the actual 32-bit atomic variable.
53  *
54  * EXAMPLE
55  *
56  *    SilcAtomic32 refcnt;
57  *
58  *    // Initialize atomic variable
59  *    silc_atomic_init32(&refcnt, 0);
60  *
61  *    ...
62  *    // Increment referene counter
63  *    silc_atomic_add_int32(&refcnt, 1);
64  *    ...
65  *
66  *    // Uninitialize atomic variable
67  *    silc_atomic_uninit32(&refcnt);
68  *
69  ***/
70
71 /****s* silcutil/SilcAtomicAPI/SilcAtomic16
72  *
73  * NAME
74  *
75  *    typedef struct { ... } SilcAtomic16;
76  *
77  * DESCRIPTION
78  *
79  *    The atomic operation structure given as argument to all atomic
80  *    operation functions.  It hols the actual 16-bit atomic variable.
81  *
82  * EXAMPLE
83  *
84  *    SilcAtomic16 refcnt;
85  *
86  *    // Initialize atomic variable
87  *    silc_atomic_init16(&refcnt, 0);
88  *
89  *    ...
90  *    // Increment referene counter
91  *    silc_atomic_add_int16(&refcnt, 1);
92  *    ...
93  *
94  *    // Uninitialize atomic variable
95  *    silc_atomic_uninit16(&refcnt);
96  *
97  ***/
98
99 /****s* silcutil/SilcAtomicAPI/SilcAtomic8
100  *
101  * NAME
102  *
103  *    typedef struct { ... } SilcAtomic8;
104  *
105  * DESCRIPTION
106  *
107  *    The atomic operation structure given as argument to all atomic
108  *    operation functions.  It hols the actual 8-bit atomic variable.
109  *
110  * EXAMPLE
111  *
112  *    SilcAtomic8 refcnt;
113  *
114  *    // Initialize atomic variable
115  *    silc_atomic_init8(&refcnt, 0);
116  *
117  *    ...
118  *    // Increment referene counter
119  *    silc_atomic_add_int8(&refcnt, 1);
120  *    ...
121  *
122  *    // Uninitialize atomic variable
123  *    silc_atomic_uninit8(&refcnt);
124  *
125  ***/
126
127 /****s* silcutil/SilcAtomicAPI/SilcAtomicPointer
128  *
129  * NAME
130  *
131  *    typedef struct { ... } SilcAtomicPointer;
132  *
133  * DESCRIPTION
134  *
135  *    The atomic operation structure given as argument to all atomic
136  *    operation functions.  It hols the actual pointer variable.
137  *
138  * EXAMPLE
139  *
140  *    SilcAtomicPointer ptr;
141  *
142  *    // Initialize atomic variable
143  *    silc_atomic_init_pointer(&ptr, NULL);
144  *
145  *    ...
146  *    // Set pointer
147  *    silc_atomic_set_pointer(&ptr, context);
148  *    ...
149  *
150  *    // Uninitialize atomic variable
151  *    silc_atomic_uninit_pointer(&ptr);
152  *
153  ***/
154
155 #if !defined(SILC_THREADS) || defined(SILC_WIN32) || (defined(__GNUC__) &&  \
156     (defined(SILC_I486) || defined(SILC_X86_64) || defined(SILC_IA64) ||    \
157      defined(SILC_POWERPC)))
158 typedef struct {
159   volatile SilcUInt32 value;
160 } SilcAtomic32;
161 typedef struct {
162   volatile void *pointer;
163 } SilcAtomicPointer;
164 #else
165 #define SILC_ATOMIC_MUTEX
166 typedef struct {
167   SilcMutex lock;
168   volatile SilcUInt32 value;
169 } SilcAtomic32;
170 typedef struct {
171   SilcMutex lock;
172   volatile void *pointer;
173 } SilcAtomicPointer;
174 #endif
175
176 #if !defined(SILC_THREADS) || (defined(__GNUC__) && (defined(SILC_I486) ||  \
177                                                      defined(SILC_X86_64)))
178 typedef struct {
179   volatile SilcUInt16 value;
180 } SilcAtomic16;
181 #elif defined(SILC_WIN32) || (defined(__GNUC__) && (defined(SILC_IA64) ||   \
182                                                     defined(SILC_POWERPC)))
183 typedef struct {
184   volatile SilcUInt32 value;
185 } SilcAtomic16;
186 #else
187 typedef struct {
188   SilcMutex lock;
189   volatile SilcUInt16 value;
190 } SilcAtomic16;
191 #endif
192
193 #if !defined(SILC_THREADS) || (defined(__GNUC__) && (defined(SILC_I486) ||  \
194                                                      defined(SILC_X86_64)))
195 typedef struct {
196   volatile SilcUInt8 value;
197 } SilcAtomic8;
198 #elif defined(SILC_WIN32) || (defined(__GNUC__) && (defined(SILC_IA64) ||   \
199                                                     defined(SILC_POWERPC)))
200 typedef struct {
201   volatile SilcUInt32 value;
202 } SilcAtomic8;
203 #else
204 typedef struct {
205   SilcMutex lock;
206   volatile SilcUInt8 value;
207 } SilcAtomic8;
208 #endif
209
210 /****f* silcutil/SilcAtomicAPI/silc_atomic_init32
211  *
212  * SYNOPSIS
213  *
214  *    static inline
215  *    SilcBool silc_atomic_init32(SilcAtomic32 *atomic, SilcUInt32 value);
216  *
217  * DESCRIPTION
218  *
219  *    Initializes the atomic variable `atomic', and sets the `value' as its
220  *    inital value.  Returns FALSE on error.  To uninitialize call the
221  *    silc_atomic_uninit32 function.
222  *
223  ***/
224
225 static inline
226 SilcBool silc_atomic_init32(SilcAtomic32 *atomic, SilcUInt32 value)
227 {
228   atomic->value = value;
229
230 #if defined(SILC_ATOMIC_MUTEX)
231   if (!silc_mutex_alloc(&atomic->lock))
232     return FALSE;
233 #endif /* SILC_ATOMIC_MUTEX */
234
235   return TRUE;
236 }
237
238 /****f* silcutil/SilcAtomicAPI/silc_atomic_init16
239  *
240  * SYNOPSIS
241  *
242  *    static inline
243  *    SilcBool silc_atomic_init16(SilcAtomic16 *atomic, SilcUInt16 value);
244  *
245  * DESCRIPTION
246  *
247  *    Initializes the atomic variable `atomic', and sets the `value' as its
248  *    inital value.  Returns FALSE on error.  To uninitialize call the
249  *    silc_atomic_uninit32 function.
250  *
251  ***/
252
253 static inline
254 SilcBool silc_atomic_init16(SilcAtomic16 *atomic, SilcUInt16 value)
255 {
256   atomic->value = value;
257
258 #if defined(SILC_ATOMIC_MUTEX)
259   if (!silc_mutex_alloc(&atomic->lock))
260     return FALSE;
261 #endif /* SILC_ATOMIC_MUTEX */
262
263   return TRUE;
264 }
265
266 /****f* silcutil/SilcAtomicAPI/silc_atomic_init8
267  *
268  * SYNOPSIS
269  *
270  *    static inline
271  *    SilcBool silc_atomic_init8(SilcAtomic8 *atomic, SilcUInt8 value);
272  *
273  * DESCRIPTION
274  *
275  *    Initializes the atomic variable `atomic', and sets the `value' as its
276  *    inital value.  Returns FALSE on error.  To uninitialize call the
277  *    silc_atomic_uninit8 function.
278  *
279  ***/
280
281 static inline
282 SilcBool silc_atomic_init8(SilcAtomic8 *atomic, SilcUInt8 value)
283 {
284   atomic->value = value;
285
286 #if defined(SILC_ATOMIC_MUTEX)
287   if (!silc_mutex_alloc(&atomic->lock))
288     return FALSE;
289 #endif /* SILC_ATOMIC_MUTEX */
290
291   return TRUE;
292 }
293
294 /****f* silcutil/SilcAtomicAPI/silc_atomic_init_pointer
295  *
296  * SYNOPSIS
297  *
298  *    static inline
299  *    SilcBool silc_atomic_init_pointer(SilcAtomicPointer *atomic,
300  *                                      void *pointer);
301  *
302  * DESCRIPTION
303  *
304  *    Initializes the atomic pointer variable `atomic', and sets the `pointer'
305  *    as its inital pointer.  Returns FALSE on error.  To uninitialize call
306  *    the silc_atomic_uninit_pointer function.
307  *
308  ***/
309
310 static inline
311 SilcBool silc_atomic_init_pointer(SilcAtomicPointer *atomic, void *pointer)
312 {
313   atomic->pointer = pointer;
314
315 #if defined(SILC_ATOMIC_MUTEX)
316   if (!silc_mutex_alloc(&atomic->lock))
317     return FALSE;
318 #endif /* SILC_ATOMIC_MUTEX */
319
320   return TRUE;
321 }
322
323 /****f* silcutil/SilcAtomicAPI/silc_atomic_uninit32
324  *
325  * SYNOPSIS
326  *
327  *    static inline
328  *    void silc_atomic_uninit32(SilcAtomic32 *atomic);
329  *
330  * DESCRIPTION
331  *
332  *    Uninitializes the atomic variable `atomic'.  This should alwyas be
333  *    called after the atomic variable is not used anymore.
334  *
335  ***/
336
337 static inline
338 void silc_atomic_uninit32(SilcAtomic32 *atomic)
339 {
340   atomic->value = 0;
341 #if defined(SILC_ATOMIC_MUTEX)
342   silc_mutex_free(atomic->lock);
343 #endif /* SILC_ATOMIC_MUTEX */
344 }
345
346 /****f* silcutil/SilcAtomicAPI/silc_atomic_uninit16
347  *
348  * SYNOPSIS
349  *
350  *    static inline
351  *    void silc_atomic_uninit16(SilcAtomic16 *atomic);
352  *
353  * DESCRIPTION
354  *
355  *    Uninitializes the atomic variable `atomic'.  This should alwyas be
356  *    called after the atomic variable is not used anymore.
357  *
358  ***/
359
360 static inline
361 void silc_atomic_uninit16(SilcAtomic16 *atomic)
362 {
363   atomic->value = 0;
364 #if defined(SILC_ATOMIC_MUTEX)
365   silc_mutex_free(atomic->lock);
366 #endif /* SILC_ATOMIC_MUTEX */
367 }
368
369 /****f* silcutil/SilcAtomicAPI/silc_atomic_uninit8
370  *
371  * SYNOPSIS
372  *
373  *    static inline
374  *    void silc_atomic_uninit8(SilcAtomic8 *atomic);
375  *
376  * DESCRIPTION
377  *
378  *    Uninitializes the atomic variable `atomic'.  This should alwyas be
379  *    called after the atomic variable is not used anymore.
380  *
381  ***/
382
383 static inline
384 void silc_atomic_uninit8(SilcAtomic8 *atomic)
385 {
386   atomic->value = 0;
387 #if defined(SILC_ATOMIC_MUTEX)
388   silc_mutex_free(atomic->lock);
389 #endif /* SILC_ATOMIC_MUTEX */
390 }
391
392 /****f* silcutil/SilcAtomicAPI/silc_atomic_uninit_pointer
393  *
394  * SYNOPSIS
395  *
396  *    static inline
397  *    void silc_atomic_uninit_pointer(SilcAtomicPointer *atomic);
398  *
399  * DESCRIPTION
400  *
401  *    Uninitializes the atomic variable `atomic'.  This should alwyas be
402  *    called after the atomic variable is not used anymore.
403  *
404  ***/
405
406 static inline
407 void silc_atomic_uninit_pointer(SilcAtomicPointer *atomic)
408 {
409   atomic->pointer = NULL;
410 #if defined(SILC_ATOMIC_MUTEX)
411   silc_mutex_free(atomic->lock);
412 #endif /* SILC_ATOMIC_MUTEX */
413 }
414
415 /****f* silcutil/SilcAtomicAPI/silc_atomic_set_int32
416  *
417  * SYNOPSIS
418  *
419  *    static inline
420  *    void silc_atomic_set_int32(SilcAtomic32 *atomic, SilcUInt32 value);
421  *
422  * DESCRIPTION
423  *
424  *    Atomically sets `value' to 32-bit integer.
425  *
426  ***/
427
428 static inline
429 void silc_atomic_set_int32(SilcAtomic32 *atomic, SilcUInt32 value)
430 {
431 #if !defined(SILC_THREADS) || defined(SILC_WIN32) ||                     \
432      (defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64)))
433   /* No threads, Windows, i486 or x86_64, no memory barrier needed */
434   atomic->value = value;
435
436 #elif defined(__GNUC__) && defined(SILC_IA64)
437   /* IA64, memory barrier needed */
438   atomic->value = value;
439   __sync_synchronize();
440
441 #elif defined(__GNUC__) && defined(SILC_POWERPC)
442   /* PowerPC, memory barrier needed */
443   atomic->value = value;
444   __asm("sync" : : : "memory");
445
446 #else
447   /* Mutex */
448   silc_mutex_lock(atomic->lock);
449   atomic->value = value;
450   silc_mutex_unlock(atomic->lock);
451 #endif
452 }
453
454 /****f* silcutil/SilcAtomicAPI/silc_atomic_set_int16
455  *
456  * SYNOPSIS
457  *
458  *    static inline
459  *    void silc_atomic_set_int16(SilcAtomic16 *atomic, SilcUInt16 value);
460  *
461  * DESCRIPTION
462  *
463  *    Atomically sets `value' to 16-bit integer.
464  *
465  ***/
466
467 static inline
468 void silc_atomic_set_int16(SilcAtomic16 *atomic, SilcUInt16 value)
469 {
470 #if !defined(SILC_THREADS) || defined(SILC_WIN32) ||                     \
471      (defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64)))
472   /* No threads, Windows, i486 or x86_64, no memory barrier needed */
473   atomic->value = value;
474
475 #elif defined(__GNUC__) && defined(SILC_IA64)
476   /* IA64, memory barrier needed */
477   atomic->value = value;
478   __sync_synchronize();
479
480 #elif defined(__GNUC__) && defined(SILC_POWERPC)
481   /* PowerPC, memory barrier needed */
482   atomic->value = value;
483   __asm("sync" : : : "memory");
484
485 #else
486   /* Mutex */
487   silc_mutex_lock(atomic->lock);
488   atomic->value = value;
489   silc_mutex_unlock(atomic->lock);
490 #endif
491 }
492
493 /****f* silcutil/SilcAtomicAPI/silc_atomic_set_int8
494  *
495  * SYNOPSIS
496  *
497  *    static inline
498  *    void silc_atomic_set_int8(SilcAtomic8 *atomic, SilcUInt8 value);
499  *
500  * DESCRIPTION
501  *
502  *    Atomically sets `value' to 8-bit integer.
503  *
504  ***/
505
506 static inline
507 void silc_atomic_set_int8(SilcAtomic8 *atomic, SilcUInt8 value)
508 {
509 #if !defined(SILC_THREADS) || defined(SILC_WIN32) ||                     \
510      (defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64)))
511   /* No threads, Windows, i486 or x86_64, no memory barrier needed */
512   atomic->value = value;
513
514 #elif defined(__GNUC__) && defined(SILC_IA64)
515   /* IA64, memory barrier needed */
516   atomic->value = value;
517   __sync_synchronize();
518
519 #elif defined(__GNUC__) && defined(SILC_POWERPC)
520   /* PowerPC, memory barrier needed */
521   atomic->value = value;
522   __asm("sync" : : : "memory");
523
524 #else
525   /* Mutex */
526   silc_mutex_lock(atomic->lock);
527   atomic->value = value;
528   silc_mutex_unlock(atomic->lock);
529 #endif
530 }
531
532 /****f* silcutil/SilcAtomicAPI/silc_atomic_set_pointer
533  *
534  * SYNOPSIS
535  *
536  *    static inline
537  *    void silc_atomic_set_pointer(SilcAtomicPointer *atomic, void *pointer);
538  *
539  * DESCRIPTION
540  *
541  *    Atomically sets `pointer' to the atomic variable.
542  *
543  ***/
544
545 static inline
546 void silc_atomic_set_pointer(SilcAtomicPointer *atomic, void *pointer)
547 {
548 #if !defined(SILC_THREADS) || defined(SILC_WIN32) ||                     \
549      (defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64)))
550   /* No threads, Windows, i486 or x86_64, no memory barrier needed */
551   atomic->pointer = pointer;
552
553 #elif defined(__GNUC__) && defined(SILC_IA64)
554   /* IA64, memory barrier needed */
555   atomic->pointer = pointer;
556   __sync_synchronize();
557
558 #elif defined(__GNUC__) && defined(SILC_POWERPC)
559   /* PowerPC, memory barrier needed */
560   atomic->pointer = pointer;
561   __asm("sync" : : : "memory");
562
563 #else
564   /* Mutex */
565   silc_mutex_lock(atomic->lock);
566   atomic->pointer = pointer;
567   silc_mutex_unlock(atomic->lock);
568 #endif
569 }
570
571 /****f* silcutil/SilcAtomicAPI/silc_atomic_get_int32
572  *
573  * SYNOPSIS
574  *
575  *    static inline
576  *    SilcUInt32 silc_atomic_get_int32(SilcAtomic32 *atomic);
577  *
578  * DESCRIPTION
579  *
580  *    Returns the current value of the atomic variable.
581  *
582  ***/
583
584 static inline
585 SilcUInt32 silc_atomic_get_int32(SilcAtomic32 *atomic)
586 {
587   SilcUInt32 ret;
588
589 #if !defined(SILC_THREADS) || defined(SILC_WIN32) ||                     \
590      (defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64)))
591   /* No threads, Windows, i486 or x86_64, no memory barrier needed */
592   ret = atomic->value;
593   return ret;
594
595 #elif defined(__GNUC__) && defined(SILC_IA64)
596   /* IA64, memory barrier needed */
597   __sync_synchronize();
598   ret = atomic->value;
599   return ret;
600
601 #elif defined(__GNUC__) && defined(SILC_POWERPC)
602   /* PowerPC, memory barrier needed */
603   __asm("sync" : : : "memory");
604   ret = atomic->value;
605   return ret;
606
607 #else
608   /* Mutex */
609   silc_mutex_lock(atomic->lock);
610   ret = atomic->value;
611   silc_mutex_unlock(atomic->lock);
612   return ret;
613 #endif
614 }
615
616 /****f* silcutil/SilcAtomicAPI/silc_atomic_get_int16
617  *
618  * SYNOPSIS
619  *
620  *    static inline
621  *    SilcUInt32 silc_atomic_get_int16(SilcAtomic16 *atomic);
622  *
623  * DESCRIPTION
624  *
625  *    Returns the current value of the atomic variable.
626  *
627  ***/
628
629 static inline
630 SilcUInt16 silc_atomic_get_int16(SilcAtomic16 *atomic)
631 {
632   SilcUInt16 ret;
633
634 #if !defined(SILC_THREADS) || defined(SILC_WIN32) ||                     \
635      (defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64)))
636   /* No threads, Windows, i486 or x86_64, no memory barrier needed */
637   ret = atomic->value & 0xffff;
638   return ret;
639
640 #elif defined(__GNUC__) && defined(SILC_IA64)
641   /* IA64, memory barrier needed */
642   __sync_synchronize();
643   ret = atomic->value & 0xffff;
644   return ret;
645
646 #elif defined(__GNUC__) && defined(SILC_POWERPC)
647   /* PowerPC, memory barrier needed */
648   __asm("sync" : : : "memory");
649   ret = atomic->value & 0xffff;
650   return ret;
651
652 #else
653   /* Mutex */
654   silc_mutex_lock(atomic->lock);
655   ret = atomic->value & 0xffff;
656   silc_mutex_unlock(atomic->lock);
657   return ret;
658 #endif
659 }
660
661 /****f* silcutil/SilcAtomicAPI/silc_atomic_get_int8
662  *
663  * SYNOPSIS
664  *
665  *    static inline
666  *    SilcUInt32 silc_atomic_get_int8(SilcAtomic8 *atomic);
667  *
668  * DESCRIPTION
669  *
670  *    Returns the current value of the atomic variable.
671  *
672  ***/
673
674 static inline
675 SilcUInt8 silc_atomic_get_int8(SilcAtomic8 *atomic)
676 {
677   SilcUInt8 ret;
678
679 #if !defined(SILC_THREADS) || defined(SILC_WIN32) ||                     \
680      (defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64)))
681   /* No threads, Windows, i486 or x86_64, no memory barrier needed */
682   ret = atomic->value & 0xff;
683   return ret;
684
685 #elif defined(__GNUC__) && defined(SILC_IA64)
686   /* IA64, memory barrier needed */
687   __sync_synchronize();
688   ret = atomic->value & 0xff;
689   return ret;
690
691 #elif defined(__GNUC__) && defined(SILC_POWERPC)
692   /* PowerPC, memory barrier needed */
693   __asm("sync" : : : "memory");
694   ret = atomic->value & 0xff;
695   return ret;
696
697 #else
698   /* Mutex */
699   silc_mutex_lock(atomic->lock);
700   ret = atomic->value & 0xff;
701   silc_mutex_unlock(atomic->lock);
702   return ret;
703 #endif
704 }
705
706 /****f* silcutil/SilcAtomicAPI/silc_atomic_get_pointer
707  *
708  * SYNOPSIS
709  *
710  *    static inline
711  *    SilcUInt8 silc_atomic_get_pointer(SilcAtomicPointer *atomic)
712  *
713  * DESCRIPTION
714  *
715  *    Returns the current pointer value of the atomic variable.
716  *
717  ***/
718
719 static inline
720 void *silc_atomic_get_pointer(SilcAtomicPointer *atomic)
721 {
722   void *ret;
723
724 #if !defined(SILC_THREADS) || defined(SILC_WIN32) ||                     \
725      (defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64)))
726   /* No threads, Windows, i486 or x86_64, no memory barrier needed */
727   ret = (void *)atomic->pointer;
728   return ret;
729
730 #elif defined(__GNUC__) && defined(SILC_IA64)
731   /* IA64, memory barrier needed */
732   __sync_synchronize();
733   ret = (void *)atomic->pointer;
734   return ret;
735
736 #elif defined(__GNUC__) && defined(SILC_POWERPC)
737   /* PowerPC, memory barrier needed */
738   __asm("sync" : : : "memory");
739   ret = (void *)atomic->pointer;
740   return ret;
741
742 #else
743   /* Mutex */
744   silc_mutex_lock(atomic->lock);
745   ret = (void *)atomic->pointer;
746   silc_mutex_unlock(atomic->lock);
747   return ret;
748 #endif
749 }
750
751 /****f* silcutil/SilcAtomicAPI/silc_atomic_add_int32
752  *
753  * SYNOPSIS
754  *
755  *    static inline
756  *    SilcUInt32 silc_atomic_add_int32(SilcAtomic32 *atomic, SilcInt32 value);
757  *
758  * DESCRIPTION
759  *
760  *    Atomically adds `value' to 32-bit integer.  Returns the value after
761  *    addition.
762  *
763  ***/
764
765 static inline
766 SilcUInt32 silc_atomic_add_int32(SilcAtomic32 *atomic, SilcInt32 value)
767 {
768   SilcUInt32 ret;
769
770 #if !defined(SILC_THREADS)
771   /* No atomic operations */
772   ret = atomic->value;
773   atomic->value += value;
774
775 #elif defined(SILC_WIN32)
776   /* Windows */
777   ret = InterlockedExchangeAdd(&atomic->value, (LONG)value);
778
779 #elif defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64))
780   /* GCC + i486 or x86_64 */
781   __asm __volatile("lock; xaddl %0, %1"
782                    : "=r" (ret), "+m" (atomic->value)
783                    : "0" (value));
784
785 #elif defined(__GNUC__) && defined(SILC_IA64)
786   /* GCC + IA64 (GCC builtin atomic operations) */
787   ret = __sync_fetch_and_add(&atomic->value, value);
788
789 #elif defined(__GNUC__) && defined(SILC_POWERPC)
790   /* GCC + PowerPC (code adapted from IBM's documentation) */
791   __asm __volatile("0: lwarx  %0,  0, %2\n"
792                    "   add    %0, %1, %0\n"
793                    "   stwcx. %0,  0, %2\n"
794                    "   bne-   0b"
795                    : "=&r" (ret)
796                    : "r" (value), "r" (&atomic->value)
797                    : "cc");
798   return ret;
799
800 #else
801   /* Mutex */
802   silc_mutex_lock(atomic->lock);
803   ret = atomic->value;
804   atomic->value += value;
805   silc_mutex_unlock(atomic->lock);
806 #endif
807
808   return ret + value;
809 }
810
811 /****f* silcutil/SilcAtomicAPI/silc_atomic_add_int32
812  *
813  * SYNOPSIS
814  *
815  *    static inline
816  *    SilcUInt16 silc_atomic_add_int16(SilcAtomic16 *atomic, SilcInt16 value);
817  *
818  * DESCRIPTION
819  *
820  *    Atomically adds `value' to 16-bit integer.  Returns the value after
821  *    addition.
822  *
823  ***/
824
825 static inline
826 SilcUInt16 silc_atomic_add_int16(SilcAtomic16 *atomic, SilcInt16 value)
827 {
828   SilcUInt16 ret;
829
830 #if !defined(SILC_THREADS)
831   /* No atomic operations */
832   ret = atomic->value;
833   atomic->value += value;
834
835 #elif defined(SILC_WIN32)
836   /* Windows */
837   LONG v = value;
838   ret = InterlockedExchangeAdd(&atomic->value, v);
839
840 #elif defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64))
841   /* GCC + i486 or x86_64 */
842   __asm __volatile("lock; xaddw %0, %1"
843                    : "=c" (ret), "+m" (atomic->value)
844                    : "0" (value));
845
846 #elif defined(__GNUC__) && defined(SILC_IA64)
847   /* GCC + IA64 (GCC builtin atomic operations) */
848   SilcInt32 v = value;
849   ret = __sync_fetch_and_add(&atomic->value, v);
850
851 #elif defined(__GNUC__) && defined(SILC_POWERPC)
852   /* GCC + PowerPC (code adapted from IBM's documentation) */
853   SilcUInt32 ret32;
854   SilcInt32 v = value;
855   __asm __volatile("0: lwarx  %0,  0, %2\n"
856                    "   add    %0, %1, %0\n"
857                    "   stwcx. %0,  0, %2\n"
858                    "   bne-   0b"
859                    : "=&r" (ret32)
860                    : "r" (v), "r" (&atomic->value)
861                    : "cc");
862   return ret32 & 0xffff;
863
864 #else
865   /* Mutex */
866   silc_mutex_lock(atomic->lock);
867   ret = atomic->value;
868   atomic->value += value;
869   silc_mutex_unlock(atomic->lock);
870 #endif
871
872   return ret + value;
873 }
874
875 /****f* silcutil/SilcAtomicAPI/silc_atomic_add_int8
876  *
877  * SYNOPSIS
878  *
879  *    static inline
880  *    SilcUInt8 silc_atomic_add_int8(SilcAtomic8 *atomic, SilcInt8 value);
881  *
882  * DESCRIPTION
883  *
884  *    Atomically adds `value' to 8-bit integer.  Returns the value after
885  *    addition.
886  *
887  ***/
888
889 static inline
890 SilcUInt8 silc_atomic_add_int8(SilcAtomic8 *atomic, SilcInt8 value)
891 {
892   SilcUInt8 ret;
893
894 #if !defined(SILC_THREADS)
895   /* No atomic operations */
896   ret = atomic->value;
897   atomic->value += value;
898
899 #elif defined(SILC_WIN32)
900   /* Windows */
901   LONG v = value;
902   ret = InterlockedExchangeAdd(&atomic->value, v);
903
904 #elif defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64))
905   /* GCC + i486 or x86_64 */
906   __asm __volatile("lock; xaddb %0, %1"
907                    : "=c" (ret), "+m" (atomic->value)
908                    : "0" (value));
909
910 #elif defined(__GNUC__) && defined(SILC_IA64)
911   /* GCC + IA64 (GCC builtin atomic operations) */
912   SilcInt32 v = value;
913   ret = __sync_fetch_and_add(&atomic->value, v);
914
915 #elif defined(__GNUC__) && defined(SILC_POWERPC)
916   /* GCC + PowerPC (code adapted from IBM's documentation) */
917   SilcUInt32 ret32;
918   SilcInt32 v = value;
919   __asm __volatile("0: lwarx  %0,  0, %2\n"
920                    "   add    %0, %1, %0\n"
921                    "   stwcx. %0,  0, %2\n"
922                    "   bne-   0b"
923                    : "=&r" (ret32)
924                    : "r" (v), "r" (&atomic->value)
925                    : "cc");
926   return ret32 & 0xff;
927
928 #else
929   /* Mutex */
930   silc_mutex_lock(atomic->lock);
931   ret = atomic->value;
932   atomic->value += value;
933   silc_mutex_unlock(atomic->lock);
934 #endif
935
936   return ret + value;
937 }
938
939 /****f* silcutil/SilcAtomicAPI/silc_atomic_sub_int32
940  *
941  * SYNOPSIS
942  *
943  *    static inline
944  *    SilcUInt32 silc_atomic_sub_int32(SilcAtomic32 *atomic, SilcInt32 value);
945  *
946  * DESCRIPTION
947  *
948  *    Atomically subtracts `value' from 32-bit integer.  Returns the value
949  *    after subtraction.
950  *
951  ***/
952
953 static inline
954 SilcUInt32 silc_atomic_sub_int32(SilcAtomic32 *atomic, SilcInt32 value)
955 {
956   return silc_atomic_add_int32(atomic, -value);
957 }
958
959 /****f* silcutil/SilcAtomicAPI/silc_atomic_sub_int16
960  *
961  * SYNOPSIS
962  *
963  *    static inline
964  *    SilcUInt16 silc_atomic_sub_int16(SilcAtomic16 *atomic, SilcInt16 value);
965  *
966  * DESCRIPTION
967  *
968  *    Atomically subtracts `value' from 16-bit integer.  Returns the value
969  *    after subtraction.
970  *
971  ***/
972
973 static inline
974 SilcUInt16 silc_atomic_sub_int16(SilcAtomic16 *atomic, SilcInt16 value)
975 {
976   return silc_atomic_add_int16(atomic, -value);
977 }
978
979 /****f* silcutil/SilcAtomicAPI/silc_atomic_sub_int8
980  *
981  * SYNOPSIS
982  *
983  *    static inline
984  *    SilcUInt8 silc_atomic_sub_int8(SilcAtomic8 *atomic, SilcInt8 value);
985  *
986  * DESCRIPTION
987  *
988  *    Atomically subtracts `value' from 8-bit integer.  Returns the value
989  *    after subtraction.
990  *
991  ***/
992
993 static inline
994 SilcUInt8 silc_atomic_sub_int8(SilcAtomic8 *atomic, SilcInt8 value)
995 {
996   return silc_atomic_add_int8(atomic, -value);
997 }
998
999 /****f* silcutil/SilcAtomicAPI/silc_atomic_cas32
1000  *
1001  * SYNOPSIS
1002  *
1003  *    static inline
1004  *    SilcBool silc_atomic_cas32(SilcAtomic32 *atomic, SilcUInt32 old_val,
1005  *                               SilcUInt32 new_val)
1006  *
1007  * DESCRIPTION
1008  *
1009  *    Performs compare and swap (CAS).  Atomically compares if the variable
1010  *    `atomic' has the value `old_val' and in that case swaps it with the
1011  *    value `new_val'.  Returns TRUE if the old value was same and it was
1012  *    swapped and FALSE if it differed and was not swapped.
1013  *
1014  ***/
1015
1016 static inline
1017 SilcBool silc_atomic_cas32(SilcAtomic32 *atomic, SilcUInt32 old_val,
1018                            SilcUInt32 new_val)
1019 {
1020   SilcUInt32 ret;
1021
1022 #if !defined(SILC_THREADS)
1023   /* No atomic operations */
1024   if (atomic->value == old_val) {
1025     atomic->value = new_val;
1026     return TRUE;
1027   }
1028   return FALSE;
1029
1030 #elif defined(SILC_WIN32)
1031   /* Windows */
1032   return InterlockedCompareExchange(&atomic->value, (LONG)new_val,
1033                                     (LONG)old_val) == old_val;
1034
1035 #elif defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64))
1036   /* GCC + i486 or x86_64 */
1037   __asm __volatile("lock; cmpxchgl %2, %1"
1038                    : "=a" (ret), "=m" (atomic->value)
1039                    : "r" (new_val), "m" (atomic->value), "0" (old_val));
1040   return ret == old_val;
1041
1042 #elif defined(__GNUC__) && defined(SILC_IA64)
1043   /* GCC + IA64 (GCC builtin atomic operations) */
1044   return  __sync_bool_compare_and_swap(&atomic->value, old_val, new_val);
1045
1046 #elif defined(__GNUC__) && defined(SILC_POWERPC)
1047   /* GCC + PowerPC */
1048   /* XXX TODO */
1049
1050 #else
1051   /* Mutex */
1052   silc_mutex_lock(atomic->lock);
1053   if (atomic->value == old_val) {
1054     atomic->value = new_val;
1055     silc_mutex_unlock(atomic->lock);
1056     return TRUE;
1057   }
1058   silc_mutex_unlock(atomic->lock);
1059   return FALSE;
1060 #endif
1061 }
1062
1063 /****f* silcutil/SilcAtomicAPI/silc_atomic_cas16
1064  *
1065  * SYNOPSIS
1066  *
1067  *    static inline
1068  *    SilcBool silc_atomic_cas16(SilcAtomic16 *atomic, SilcUInt16 old_val,
1069  *                               SilcUInt16 new_val)
1070  *
1071  * DESCRIPTION
1072  *
1073  *    Performs compare and swap (CAS).  Atomically compares if the variable
1074  *    `atomic' has the value `old_val' and in that case swaps it with the
1075  *    value `new_val'.  Returns TRUE if the old value was same and it was
1076  *    swapped and FALSE if it differed and was not swapped.
1077  *
1078  ***/
1079
1080 static inline
1081 SilcBool silc_atomic_cas16(SilcAtomic16 *atomic, SilcUInt16 old_val,
1082                            SilcUInt16 new_val)
1083 {
1084   SilcUInt16 ret;
1085
1086 #if !defined(SILC_THREADS)
1087   /* No atomic operations */
1088   if (atomic->value == old_val) {
1089     atomic->value = new_val;
1090     return TRUE;
1091   }
1092   return FALSE;
1093
1094 #elif defined(SILC_WIN32)
1095   /* Windows */
1096   LONG o = old_val, n = new_val;
1097   return InterlockedCompareExchange(&atomic->value, n, o) == o;
1098
1099 #elif defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64))
1100   /* GCC + i486 or x86_64 */
1101   __asm __volatile("lock; cmpxchgw %2, %1"
1102                    : "=a" (ret), "=m" (atomic->value)
1103                    : "c" (new_val), "m" (atomic->value), "0" (old_val));
1104   return ret == old_val;
1105
1106 #elif defined(__GNUC__) && defined(SILC_IA64)
1107   /* GCC + IA64 (GCC builtin atomic operations) */
1108   SilcUInt32 o = old_val, n = new_val;
1109   return  __sync_bool_compare_and_swap(&atomic->value, o, n);
1110
1111 #elif defined(__GNUC__) && defined(SILC_POWERPC)
1112   /* GCC + PowerPC */
1113   /* XXX TODO */
1114
1115 #else
1116   /* Mutex */
1117   silc_mutex_lock(atomic->lock);
1118   if (atomic->value == old_val) {
1119     atomic->value = new_val;
1120     silc_mutex_unlock(atomic->lock);
1121     return TRUE;
1122   }
1123   silc_mutex_unlock(atomic->lock);
1124   return FALSE;
1125 #endif
1126 }
1127
1128 /****f* silcutil/SilcAtomicAPI/silc_atomic_cas8
1129  *
1130  * SYNOPSIS
1131  *
1132  *    static inline
1133  *    SilcBool silc_atomic_cas8(SilcAtomic8 *atomic, SilcUInt8 old_val,
1134  *                              SilcUInt8 new_val)
1135  *
1136  * DESCRIPTION
1137  *
1138  *    Performs compare and swap (CAS).  Atomically compares if the variable
1139  *    `atomic' has the value `old_val' and in that case swaps it with the
1140  *    value `new_val'.  Returns TRUE if the old value was same and it was
1141  *    swapped and FALSE if it differed and was not swapped.
1142  *
1143  ***/
1144
1145 static inline
1146 SilcBool silc_atomic_cas8(SilcAtomic8 *atomic, SilcUInt8 old_val,
1147                           SilcUInt8 new_val)
1148 {
1149   SilcUInt8 ret;
1150
1151 #if !defined(SILC_THREADS)
1152   /* No atomic operations */
1153   if (atomic->value == old_val) {
1154     atomic->value = new_val;
1155     return TRUE;
1156   }
1157   return FALSE;
1158
1159 #elif defined(SILC_WIN32)
1160   /* Windows */
1161   LONG o = old_val, n = new_val;
1162   return InterlockedCompareExchange(&atomic->value, n, o) == o;
1163
1164 #elif defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64))
1165   /* GCC + i486 or x86_64 */
1166   __asm __volatile("lock; cmpxchgb %2, %1"
1167                    : "=a" (ret), "=m" (atomic->value)
1168                    : "c" (new_val), "m" (atomic->value), "0" (old_val));
1169   return ret == old_val;
1170
1171 #elif defined(__GNUC__) && defined(SILC_IA64)
1172   /* GCC + IA64 (GCC builtin atomic operations) */
1173   SilcUInt32 o = old_val, n = new_val;
1174   return  __sync_bool_compare_and_swap(&atomic->value, o, n);
1175
1176 #elif defined(__GNUC__) && defined(SILC_POWERPC)
1177   /* GCC + PowerPC */
1178   /* XXX TODO */
1179
1180 #else
1181   /* Mutex */
1182   silc_mutex_lock(atomic->lock);
1183   if (atomic->value == old_val) {
1184     atomic->value = new_val;
1185     silc_mutex_unlock(atomic->lock);
1186     return TRUE;
1187   }
1188   silc_mutex_unlock(atomic->lock);
1189   return FALSE;
1190 #endif
1191 }
1192
1193 /****f* silcutil/SilcAtomicAPI/silc_atomic_cas_pointer
1194  *
1195  * SYNOPSIS
1196  *
1197  *    static inline
1198  *    SilcBool silc_atomic_cas_pointer(SilcAtomicPointer *atomic,
1199  *                                     void *old_ptr, void *new_ptr);
1200  *
1201  * DESCRIPTION
1202  *
1203  *    Performs compare and swap (CAS).  Atomically compares if the variable
1204  *    `atomic' has the pointer `old_ptr' and in that case swaps it with the
1205  *    pointer `new_ptr'.  Returns TRUE if the old pointer was same and it was
1206  *    swapped and FALSE if it differed and was not swapped.
1207  *
1208  ***/
1209
1210 static inline
1211 SilcBool silc_atomic_cas_pointer(SilcAtomicPointer *atomic, void *old_val,
1212                                  void *new_val)
1213 {
1214   void *ret;
1215
1216 #if !defined(SILC_THREADS)
1217   /* No atomic operations */
1218   if (atomic->pointer == old_val) {
1219     atomic->pointer = new_val;
1220     return TRUE;
1221   }
1222   return FALSE;
1223
1224 #elif defined(SILC_WIN32)
1225   /* Windows */
1226   return InterlockedCompareExchangePointer(&atomic->pointer, n, o) == o;
1227
1228 #elif defined(__GNUC__) && defined(SILC_I486)
1229   /* GCC + i486 */
1230   __asm __volatile("lock; cmpxchgl %2, %1"
1231                    : "=a" (ret), "=m" (atomic->pointer)
1232                    : "c" (new_val), "m" (atomic->pointer), "0" (old_val));
1233   return ret == old_val;
1234
1235 #elif defined(__GNUC__) && defined(SILC_X86_64)
1236   /* GCC + x86_64 */
1237   __asm __volatile("lock; cmpxchgq %q2, %1"
1238                    : "=a" (ret), "=m" (atomic->pointer)
1239                    : "c" (new_val), "m" (atomic->pointer), "0" (old_val));
1240   return ret == old_val;
1241
1242 #elif defined(__GNUC__) && defined(SILC_IA64)
1243   /* GCC + IA64 (GCC builtin atomic operations) */
1244   return  __sync_bool_compare_and_swap((long)&atomic->pointer, (long)old_val,
1245                                        (long)new_val);
1246
1247 #elif defined(__GNUC__) && defined(SILC_POWERPC)
1248   /* GCC + PowerPC */
1249   /* XXX TODO */
1250
1251 #else
1252   /* Mutex */
1253   silc_mutex_lock(atomic->lock);
1254   if (atomic->pointer == old_val) {
1255     atomic->pointer = new_val;
1256     silc_mutex_unlock(atomic->lock);
1257     return TRUE;
1258   }
1259   silc_mutex_unlock(atomic->lock);
1260   return FALSE;
1261 #endif
1262 }
1263
1264 #endif /* SILCATOMIC_H */