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