Reorganized silcatomi.h.
[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 }
376 #endif /* SILC_ATOMIC_MUTEX */
377
378 SILC_ATOMIC_UNINIT(8, 8);
379 SILC_ATOMIC_UNINIT(16, 16);
380 SILC_ATOMIC_UNINIT(32, 32);
381 SILC_ATOMIC_UNINIT(_pointer, Pointer);
382
383 /****f* silcutil/SilcAtomicAPI/silc_atomic_set_int32
384  *
385  * SYNOPSIS
386  *
387  *    static inline
388  *    void silc_atomic_set_int32(SilcAtomic32 *atomic, SilcUInt32 value);
389  *
390  * DESCRIPTION
391  *
392  *    Atomically sets `value' to 32-bit integer.
393  *
394  ***/
395
396 /****f* silcutil/SilcAtomicAPI/silc_atomic_set_int16
397  *
398  * SYNOPSIS
399  *
400  *    static inline
401  *    void silc_atomic_set_int16(SilcAtomic16 *atomic, SilcUInt16 value);
402  *
403  * DESCRIPTION
404  *
405  *    Atomically sets `value' to 16-bit integer.
406  *
407  ***/
408
409 /****f* silcutil/SilcAtomicAPI/silc_atomic_set_int8
410  *
411  * SYNOPSIS
412  *
413  *    static inline
414  *    void silc_atomic_set_int8(SilcAtomic8 *atomic, SilcUInt8 value);
415  *
416  * DESCRIPTION
417  *
418  *    Atomically sets `value' to 8-bit integer.
419  *
420  ***/
421
422 #define SILC_ATOMIC_SET_INT_F(bits)                                     \
423 static inline void silc_atomic_set_int##bits(SilcAtomic##bits *atomic,  \
424                                              SilcUInt##bits value)
425
426 #if !defined(SILC_THREADS)
427 #define SILC_ATOMIC_SET_INT(bits, bp, bp2)                              \
428 SILC_ATOMIC_SET_INT_F(bits)                                             \
429 {                                                                       \
430   /* No atomic operations */                                            \
431   atomic->value = value;                                                \
432 }
433
434 #elif defined(SILC_WIN32)
435 #define SILC_ATOMIC_SET_INT(bits, bp, bp2)                              \
436 SILC_ATOMIC_SET_INT_F(bits)                                             \
437 {                                                                       \
438   /* Windows */                                                         \
439   InterlockedExchange((LONG)&atomic->value, (LONG)value);               \
440 }
441
442 #elif defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64))
443 #define SILC_ATOMIC_SET_INT(bits, bp, bp2)                              \
444 SILC_ATOMIC_SET_INT_F(bits)                                             \
445 {                                                                       \
446   /* GCC + i486 or x86_64 */                                            \
447   __asm __volatile("xchg" bp " %" bp2 "0, %1"                           \
448                    : "=r" (value)                                       \
449                    : "m" (atomic->value), "0" (value));                 \
450 }
451
452 #elif defined(__GNUC__) && defined(SILC_IA64)
453 #define SILC_ATOMIC_SET_INT(bits, bp, bp2)                              \
454 SILC_ATOMIC_SET_INT_F(bits)                                             \
455 {                                                                       \
456   /* IA64, memory barrier needed */                                     \
457   atomic->value = value;                                                \
458   __sync_synchronize();                                                 \
459 }
460
461 #elif defined(__GNUC__) && defined(SILC_POWERPC)
462 #define SILC_ATOMIC_SET_INT(bits, bp, bp2)                              \
463 SILC_ATOMIC_SET_INT_F(bits)                                             \
464 {                                                                       \
465   /* PowerPC, memory barrier needed */                                  \
466   atomic->value = value;                                                \
467   __asm("sync" : : : "memory");                                         \
468 }
469
470 #else /* SILC_ATOMIC_MUTEX */
471 #define SILC_ATOMIC_SET_INT(bits, bp, bp2)                              \
472 SILC_ATOMIC_SET_INT_F(bits)                                             \
473 {                                                                       \
474   /* Mutex */                                                           \
475   silc_mutex_lock(atomic->lock);                                        \
476   atomic->value = value;                                                \
477   silc_mutex_unlock(atomic->lock);                                      \
478 }
479 #endif /* !SILC_THREADS */
480
481 SILC_ATOMIC_SET_INT(8, "b", "b");
482 SILC_ATOMIC_SET_INT(16, "w", "w");
483 SILC_ATOMIC_SET_INT(32, "l", "");
484
485 /****f* silcutil/SilcAtomicAPI/silc_atomic_set_pointer
486  *
487  * SYNOPSIS
488  *
489  *    static inline
490  *    void silc_atomic_set_pointer(SilcAtomicPointer *atomic, void *pointer);
491  *
492  * DESCRIPTION
493  *
494  *    Atomically sets `pointer' to the atomic variable.
495  *
496  ***/
497
498 static inline
499 void silc_atomic_set_pointer(SilcAtomicPointer *atomic, void *pointer)
500 {
501 #if !defined(SILC_THREADS) ||                    \
502      (defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64)))
503   /* No threads, Windows, i486 or x86_64, no memory barrier needed */
504   atomic->value = pointer;
505
506 #elif defined(SILC_WIN32)
507   InterlockedExchangePointer(&atomic->value, pointer);
508
509 #elif defined(__GNUC__) && defined(SILC_IA64)
510   /* IA64, memory barrier needed */
511   atomic->value = pointer;
512   __sync_synchronize();
513
514 #elif defined(__GNUC__) && defined(SILC_POWERPC)
515   /* PowerPC, memory barrier needed */
516   atomic->value = pointer;
517   __asm("sync" : : : "memory");
518
519 #else
520   /* Mutex */
521   silc_mutex_lock(atomic->lock);
522   atomic->value = pointer;
523   silc_mutex_unlock(atomic->lock);
524 #endif
525 }
526
527 /****f* silcutil/SilcAtomicAPI/silc_atomic_get_int32
528  *
529  * SYNOPSIS
530  *
531  *    static inline
532  *    SilcUInt32 silc_atomic_get_int32(SilcAtomic32 *atomic);
533  *
534  * DESCRIPTION
535  *
536  *    Returns the current value of the atomic variable.
537  *
538  ***/
539
540 /****f* silcutil/SilcAtomicAPI/silc_atomic_get_int16
541  *
542  * SYNOPSIS
543  *
544  *    static inline
545  *    SilcUInt32 silc_atomic_get_int16(SilcAtomic16 *atomic);
546  *
547  * DESCRIPTION
548  *
549  *    Returns the current value of the atomic variable.
550  *
551  ***/
552
553 /****f* silcutil/SilcAtomicAPI/silc_atomic_get_int8
554  *
555  * SYNOPSIS
556  *
557  *    static inline
558  *    SilcUInt32 silc_atomic_get_int8(SilcAtomic8 *atomic);
559  *
560  * DESCRIPTION
561  *
562  *    Returns the current value of the atomic variable.
563  *
564  ***/
565
566 #define SILC_ATOMIC_GET_INT_F(bits)                                     \
567 static inline                                                           \
568 SilcUInt##bits silc_atomic_get_int##bits(SilcAtomic##bits *atomic)
569
570 #if !defined(SILC_THREADS) || defined(SILC_WIN32) ||                    \
571      (defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64)))
572 #define SILC_ATOMIC_GET_INT(bits)                                       \
573 SILC_ATOMIC_GET_INT_F(bits)                                             \
574 {                                                                       \
575   SilcUInt##bits ret;                                                   \
576                                                                         \
577   /* No threads, Windows, i486 or x86_64, no memory barrier needed */   \
578   ret = atomic->value;                                                  \
579   return ret;                                                           \
580 }
581
582 #elif defined(__GNUC__) && defined(SILC_IA64)
583 #define SILC_ATOMIC_GET_INT(bits)                                       \
584 SILC_ATOMIC_GET_INT_F(bits)                                             \
585 {                                                                       \
586   SilcUInt##bits ret;                                                   \
587                                                                         \
588   /* IA64, memory barrier needed */                                     \
589   __sync_synchronize();                                                 \
590   ret = atomic->value;                                                  \
591   return ret;                                                           \
592 }
593
594 #elif defined(__GNUC__) && defined(SILC_POWERPC)
595 #define SILC_ATOMIC_GET_INT(bits)                                       \
596 SILC_ATOMIC_GET_INT_F(bits)                                             \
597 {                                                                       \
598   SilcUInt##bits ret;                                                   \
599                                                                         \
600   /* PowerPC, memory barrier needed */                                  \
601   __asm("sync" : : : "memory");                                         \
602   ret = atomic->value;                                                  \
603   return ret;                                                           \
604 }
605
606 #else /* SILC_ATOMIC_MUTEX */
607 #define SILC_ATOMIC_GET_INT(bits)                                       \
608 SILC_ATOMIC_GET_INT_F(bits)                                             \
609 {                                                                       \
610   SilcUInt##bits ret;                                                   \
611                                                                         \
612   /* Mutex */                                                           \
613   silc_mutex_lock(atomic->lock);                                        \
614   ret = atomic->value;                                                  \
615   silc_mutex_unlock(atomic->lock);                                      \
616   return ret;                                                           \
617 }
618 #endif /* !SILC_THREADS */
619
620 SILC_ATOMIC_GET_INT(8);
621 SILC_ATOMIC_GET_INT(16);
622 SILC_ATOMIC_GET_INT(32);
623
624 /****f* silcutil/SilcAtomicAPI/silc_atomic_get_pointer
625  *
626  * SYNOPSIS
627  *
628  *    static inline
629  *    SilcUInt8 silc_atomic_get_pointer(SilcAtomicPointer *atomic)
630  *
631  * DESCRIPTION
632  *
633  *    Returns the current pointer value of the atomic variable.
634  *
635  ***/
636
637 static inline
638 void *silc_atomic_get_pointer(SilcAtomicPointer *atomic)
639 {
640   void *ret;
641
642 #if !defined(SILC_THREADS) || defined(SILC_WIN32) ||                     \
643      (defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64)))
644   /* No threads, Windows, i486 or x86_64, no memory barrier needed */
645   ret = (void *)atomic->value;
646   return ret;
647
648 #elif defined(__GNUC__) && defined(SILC_IA64)
649   /* IA64, memory barrier needed */
650   __sync_synchronize();
651   ret = (void *)atomic->value;
652   return ret;
653
654 #elif defined(__GNUC__) && defined(SILC_POWERPC)
655   /* PowerPC, memory barrier needed */
656   __asm("sync" : : : "memory");
657   ret = (void *)atomic->value;
658   return ret;
659
660 #else
661   /* Mutex */
662   silc_mutex_lock(atomic->lock);
663   ret = (void *)atomic->value;
664   silc_mutex_unlock(atomic->lock);
665   return ret;
666 #endif
667 }
668
669 /****f* silcutil/SilcAtomicAPI/silc_atomic_add_int32
670  *
671  * SYNOPSIS
672  *
673  *    static inline
674  *    SilcUInt32 silc_atomic_add_int32(SilcAtomic32 *atomic, SilcInt32 value);
675  *
676  * DESCRIPTION
677  *
678  *    Atomically adds `value' to 32-bit integer.  Returns the value after
679  *    addition.
680  *
681  ***/
682
683 /****f* silcutil/SilcAtomicAPI/silc_atomic_add_int32
684  *
685  * SYNOPSIS
686  *
687  *    static inline
688  *    SilcUInt16 silc_atomic_add_int16(SilcAtomic16 *atomic, SilcInt16 value);
689  *
690  * DESCRIPTION
691  *
692  *    Atomically adds `value' to 16-bit integer.  Returns the value after
693  *    addition.
694  *
695  ***/
696
697 /****f* silcutil/SilcAtomicAPI/silc_atomic_add_int8
698  *
699  * SYNOPSIS
700  *
701  *    static inline
702  *    SilcUInt8 silc_atomic_add_int8(SilcAtomic8 *atomic, SilcInt8 value);
703  *
704  * DESCRIPTION
705  *
706  *    Atomically adds `value' to 8-bit integer.  Returns the value after
707  *    addition.
708  *
709  ***/
710
711 #define SILC_ATOMIC_ADD_INT_F(bits)                                     \
712 static inline                                                           \
713 SilcUInt##bits silc_atomic_add_int##bits(SilcAtomic##bits *atomic,      \
714                                          SilcInt##bits value)
715
716 #if !defined(SILC_THREADS)
717 #define SILC_ATOMIC_ADD_INT(bits, bp)                                   \
718 SILC_ATOMIC_ADD_INT_F(bits)                                             \
719 {                                                                       \
720   SilcUInt##bits ret;                                                   \
721   /* No atomic operations */                                            \
722   ret = atomic->value;                                                  \
723   atomic->value += value;                                               \
724   return ret + value;                                                   \
725 }
726
727 #elif defined(SILC_WIN32)
728 #define SILC_ATOMIC_ADD_INT(bits, bp)                                   \
729 SILC_ATOMIC_ADD_INT_F(bits)                                             \
730 {                                                                       \
731   SilcUInt##bits ret;                                                   \
732   LONG val = value;                                                     \
733   /* Windows */                                                         \
734   ret = InterlockedExchangeAdd(&atomic->value, val);                    \
735   return ret + value;                                                   \
736 }
737
738 #elif defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64))
739 #define SILC_ATOMIC_ADD_INT(bits, bp)                                   \
740 SILC_ATOMIC_ADD_INT_F(bits)                                             \
741 {                                                                       \
742   SilcUInt##bits ret;                                                   \
743   /* GCC + i486 or x86_64 */                                            \
744   __asm __volatile(SILC_SMP_LOCK "xadd" bp " %0, %1"                    \
745                    : "=r" (ret), "+m" (atomic->value)                   \
746                    : "0" (value));                                      \
747   return ret + value;                                                   \
748 }
749
750 #elif defined(__GNUC__) && defined(SILC_IA64)
751 #define SILC_ATOMIC_ADD_INT(bits, bp)                                   \
752 SILC_ATOMIC_ADD_INT_F(bits)                                             \
753 {                                                                       \
754   SilcUInt##bits ret;                                                   \
755   SilcInt32 val = value;
756   /* GCC + IA64 (GCC builtin atomic operations) */                      \
757   ret = __sync_fetch_and_add(&atomic->value, val);                      \
758   return ret + value;                                                   \
759 }
760
761 #elif defined(__GNUC__) && defined(SILC_POWERPC)
762 #define SILC_ATOMIC_ADD_INT(bits, bp)                                   \
763 SILC_ATOMIC_ADD_INT_F(bits)                                             \
764 {                                                                       \
765   SilcUInt32 ret;                                                       \
766   SilcInt32 val = value;                                                \
767   /* GCC + PowerPC (code adapted from IBM's documentation) */           \
768   __asm __volatile("0: lwarx  %0,  0, %2\n"                             \
769                    "   add    %0, %1, %0\n"                             \
770                    "   stwcx. %0,  0, %2\n"                             \
771                    "   bne-   0b"                                       \
772                    : "=&r" (ret)                                        \
773                    : "r" (val), "r" (&atomic->value)                    \
774                    : "cc");                                             \
775   return ret;                                                           \
776 }
777
778 #else /* SILC_ATOMIC_MUTEX */
779 #define SILC_ATOMIC_ADD_INT(bits, bp)                                   \
780 SILC_ATOMIC_ADD_INT_F(bits)                                             \
781 {                                                                       \
782   SilcUInt##bits ret;                                                   \
783   /* Mutex */                                                           \
784   silc_mutex_lock(atomic->lock);                                        \
785   ret = atomic->value;                                                  \
786   atomic->value += value;                                               \
787   silc_mutex_unlock(atomic->lock);                                      \
788   return ret + value;                                                   \
789 }
790 #endif /* !SILC_THREADS */
791
792 SILC_ATOMIC_ADD_INT(8, "b");
793 SILC_ATOMIC_ADD_INT(16, "w");
794 SILC_ATOMIC_ADD_INT(32, "l");
795
796 /****f* silcutil/SilcAtomicAPI/silc_atomic_sub_int32
797  *
798  * SYNOPSIS
799  *
800  *    static inline
801  *    SilcUInt32 silc_atomic_sub_int32(SilcAtomic32 *atomic, SilcInt32 value);
802  *
803  * DESCRIPTION
804  *
805  *    Atomically subtracts `value' from 32-bit integer.  Returns the value
806  *    after subtraction.
807  *
808  ***/
809
810 /****f* silcutil/SilcAtomicAPI/silc_atomic_sub_int16
811  *
812  * SYNOPSIS
813  *
814  *    static inline
815  *    SilcUInt16 silc_atomic_sub_int16(SilcAtomic16 *atomic, SilcInt16 value);
816  *
817  * DESCRIPTION
818  *
819  *    Atomically subtracts `value' from 16-bit integer.  Returns the value
820  *    after subtraction.
821  *
822  ***/
823
824 /****f* silcutil/SilcAtomicAPI/silc_atomic_sub_int8
825  *
826  * SYNOPSIS
827  *
828  *    static inline
829  *    SilcUInt8 silc_atomic_sub_int8(SilcAtomic8 *atomic, SilcInt8 value);
830  *
831  * DESCRIPTION
832  *
833  *    Atomically subtracts `value' from 8-bit integer.  Returns the value
834  *    after subtraction.
835  *
836  ***/
837
838 #define silc_atomic_sub_int8(a, v) silc_atomic_add_int8(a, (-v))
839 #define silc_atomic_sub_int16(a, v) silc_atomic_add_int16(a, (-v))
840 #define silc_atomic_sub_int32(a, v) silc_atomic_add_int32(a, (-v))
841
842 /****f* silcutil/SilcAtomicAPI/silc_atomic_inc32
843  *
844  * SYNOPSIS
845  *
846  *    static inline
847  *    void silc_atomic_inc32(SilcAtomic32 *atomic);
848  *
849  * DESCRIPTION
850  *
851  *    Atomically increments 32-bit integer by one.
852  *
853  ***/
854
855 /****f* silcutil/SilcAtomicAPI/silc_atomic_inc16
856  *
857  * SYNOPSIS
858  *
859  *    static inline
860  *    void silc_atomic_inc16(SilcAtomic16 *atomic);
861  *
862  * DESCRIPTION
863  *
864  *    Atomically increments 16-bit integer by one.
865  *
866  ***/
867
868 /****f* silcutil/SilcAtomicAPI/silc_atomic_inc8
869  *
870  * SYNOPSIS
871  *
872  *    static inline
873  *    void silc_atomic_inc8(SilcAtomic8 *atomic);
874  *
875  * DESCRIPTION
876  *
877  *    Atomically increments 8-bit integer by one.
878  *
879  ***/
880
881 #define SILC_ATOMIC_INC_F(bits)                                         \
882 static inline void silc_atomic_inc##bits(SilcAtomic##bits *atomic)
883
884 #if !defined(SILC_THREADS)
885 #define SILC_ATOMIC_INC(bits, bp)                                       \
886 SILC_ATOMIC_INC_F(bits)                                                 \
887 {                                                                       \
888   /* No atomic operations */                                            \
889   ++atomic->value;                                                      \
890 }
891
892 #elif defined(SILC_WIN32)
893 #define SILC_ATOMIC_INC(bits, bp)                                       \
894 SILC_ATOMIC_INC_F(bits)                                                 \
895 {                                                                       \
896   /* Windows */                                                         \
897   InterlockedIncrement((LONG)&atomic->value);                           \
898 }
899
900 #elif defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64))
901 #define SILC_ATOMIC_INC(bits, bp)                                       \
902 SILC_ATOMIC_INC_F(bits)                                                 \
903 {                                                                       \
904   /* GCC + i486 or x86_64 */                                            \
905   __asm __volatile(SILC_SMP_LOCK "inc" bp " %0"                         \
906                    : "+m" (atomic->value));                             \
907 }
908
909 #elif defined(__GNUC__) && defined(SILC_IA64)
910 #define SILC_ATOMIC_INC(bits, bp)                                       \
911 SILC_ATOMIC_INC_F(bits)                                                 \
912 {                                                                       \
913   /* GCC + IA64 (GCC builtin atomic operations) */                      \
914   __sync_fetch_and_add(&atomic->value, 1);                              \
915 }
916
917 #elif defined(__GNUC__) && defined(SILC_POWERPC)
918 #define SILC_ATOMIC_INC(bits, bp)                                       \
919 SILC_ATOMIC_INC_F(bits)                                                 \
920 {                                                                       \
921   SilcUInt32 ret;                                                       \
922   SilcInt32 val = 1;                                                    \
923   /* GCC + PowerPC (code adapted from IBM's documentation) */           \
924   __asm __volatile("0: lwarx  %0,  0, %2\n"                             \
925                    "   add    %0, %1, %0\n"                             \
926                    "   stwcx. %0,  0, %2\n"                             \
927                    "   bne-   0b"                                       \
928                    : "=&r" (ret)                                        \
929                    : "r" (val), "r" (&atomic->value)                    \
930                    : "cc");                                             \
931 }
932
933 #else /* SILC_ATOMIC_MUTEX */
934 #define SILC_ATOMIC_INC(bits, bp)                                       \
935 SILC_ATOMIC_INC_F(bits)                                                 \
936 {                                                                       \
937   /* Mutex */                                                           \
938   silc_mutex_lock(atomic->lock);                                        \
939   ++atomic->value;                                                      \
940   silc_mutex_unlock(atomic->lock);                                      \
941 }
942 #endif /* !SILC_THREADS */
943
944 SILC_ATOMIC_INC(8, "b");
945 SILC_ATOMIC_INC(16, "w");
946 SILC_ATOMIC_INC(32, "l");
947
948 /****f* silcutil/SilcAtomicAPI/silc_atomic_dec32
949  *
950  * SYNOPSIS
951  *
952  *    static inline
953  *    void silc_atomic_dec32(SilcAtomic32 *atomic);
954  *
955  * DESCRIPTION
956  *
957  *    Atomically decrements 32-bit integer by one.
958  *
959  ***/
960
961 /****f* silcutil/SilcAtomicAPI/silc_atomic_dec16
962  *
963  * SYNOPSIS
964  *
965  *    static inline
966  *    void silc_atomic_dec16(SilcAtomic16 *atomic);
967  *
968  * DESCRIPTION
969  *
970  *    Atomically decrements 16-bit integer by one.
971  *
972  ***/
973
974 /****f* silcutil/SilcAtomicAPI/silc_atomic_dec8
975  *
976  * SYNOPSIS
977  *
978  *    static inline
979  *    void silc_atomic_dec8(SilcAtomic8 *atomic);
980  *
981  * DESCRIPTION
982  *
983  *    Atomically decrements 8-bit integer by one.
984  *
985  ***/
986
987 #define SILC_ATOMIC_DEC_F(bits)                                         \
988 static inline void silc_atomic_dec##bits(SilcAtomic##bits *atomic)
989
990 #if !defined(SILC_THREADS)
991 #define SILC_ATOMIC_DEC(bits, bp)                                       \
992 SILC_ATOMIC_DEC_F(bits)                                                 \
993 {                                                                       \
994   /* No atomic operations */                                            \
995   --atomic->value;                                                      \
996 }
997
998 #elif defined(SILC_WIN32)
999 #define SILC_ATOMIC_DEC(bits, bp)                                       \
1000 SILC_ATOMIC_DEC_F(bits)                                                 \
1001 {                                                                       \
1002   /* Windows */                                                         \
1003   InterlockedDecrement((LONG)&atomic->value);                           \
1004 }
1005
1006 #elif defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64))
1007 #define SILC_ATOMIC_DEC(bits, bp)                                       \
1008 SILC_ATOMIC_DEC_F(bits)                                                 \
1009 {                                                                       \
1010   /* GCC + i486 or x86_64 */                                            \
1011   __asm __volatile(SILC_SMP_LOCK "dec" bp " %0"                         \
1012                    : "+m" (atomic->value));                             \
1013 }
1014
1015 #elif defined(__GNUC__) && defined(SILC_IA64)
1016 #define SILC_ATOMIC_DEC(bits, bp)                                       \
1017 SILC_ATOMIC_DEC_F(bits)                                                 \
1018 {                                                                       \
1019   /* GCC + IA64 (GCC builtin atomic operations) */                      \
1020   __sync_fetch_and_sub(&atomic->value, 1);                              \
1021 }
1022
1023 #elif defined(__GNUC__) && defined(SILC_POWERPC)
1024 #define SILC_ATOMIC_DEC(bits, bp)                                       \
1025 SILC_ATOMIC_DEC_F(bits)                                                 \
1026 {                                                                       \
1027   SilcUInt32 ret;                                                       \
1028   SilcInt32 val = -1;                                                   \
1029   /* GCC + PowerPC (code adapted from IBM's documentation) */           \
1030   __asm __volatile("0: lwarx  %0,  0, %2\n"                             \
1031                    "   add    %0, %1, %0\n"                             \
1032                    "   stwcx. %0,  0, %2\n"                             \
1033                    "   bne-   0b"                                       \
1034                    : "=&r" (ret)                                        \
1035                    : "r" (val), "r" (&atomic->value)                    \
1036                    : "cc");                                             \
1037 }
1038
1039 #else /* SILC_ATOMIC_MUTEX */
1040 #define SILC_ATOMIC_DEC(bits, bp)                                       \
1041 SILC_ATOMIC_DEC_F(bits)                                                 \
1042 {                                                                       \
1043   /* Mutex */                                                           \
1044   silc_mutex_lock(atomic->lock);                                        \
1045   --atomic->value;                                                      \
1046   silc_mutex_unlock(atomic->lock);                                      \
1047 }
1048 #endif /* !SILC_THREADS */
1049
1050 SILC_ATOMIC_DEC(8, "b");
1051 SILC_ATOMIC_DEC(16, "w");
1052 SILC_ATOMIC_DEC(32, "l");
1053
1054 /****f* silcutil/SilcAtomicAPI/silc_atomic_cas32
1055  *
1056  * SYNOPSIS
1057  *
1058  *    static inline
1059  *    SilcBool silc_atomic_cas32(SilcAtomic32 *atomic, SilcUInt32 old_val,
1060  *                               SilcUInt32 new_val)
1061  *
1062  * DESCRIPTION
1063  *
1064  *    Performs compare and swap (CAS).  Atomically compares if the variable
1065  *    `atomic' has the value `old_val' and in that case swaps it with the
1066  *    value `new_val'.  Returns TRUE if the old value was same and it was
1067  *    swapped and FALSE if it differed and was not swapped.
1068  *
1069  ***/
1070
1071 /****f* silcutil/SilcAtomicAPI/silc_atomic_cas16
1072  *
1073  * SYNOPSIS
1074  *
1075  *    static inline
1076  *    SilcBool silc_atomic_cas16(SilcAtomic16 *atomic, SilcUInt16 old_val,
1077  *                               SilcUInt16 new_val)
1078  *
1079  * DESCRIPTION
1080  *
1081  *    Performs compare and swap (CAS).  Atomically compares if the variable
1082  *    `atomic' has the value `old_val' and in that case swaps it with the
1083  *    value `new_val'.  Returns TRUE if the old value was same and it was
1084  *    swapped and FALSE if it differed and was not swapped.
1085  *
1086  ***/
1087
1088 /****f* silcutil/SilcAtomicAPI/silc_atomic_cas8
1089  *
1090  * SYNOPSIS
1091  *
1092  *    static inline
1093  *    SilcBool silc_atomic_cas8(SilcAtomic8 *atomic, SilcUInt8 old_val,
1094  *                              SilcUInt8 new_val)
1095  *
1096  * DESCRIPTION
1097  *
1098  *    Performs compare and swap (CAS).  Atomically compares if the variable
1099  *    `atomic' has the value `old_val' and in that case swaps it with the
1100  *    value `new_val'.  Returns TRUE if the old value was same and it was
1101  *    swapped and FALSE if it differed and was not swapped.
1102  *
1103  ***/
1104
1105 #define SILC_ATOMIC_CAS_F(bits)                                         \
1106 static inline SilcBool silc_atomic_cas##bits(SilcAtomic##bits *atomic,  \
1107                                              SilcInt##bits old_val,     \
1108                                              SilcInt##bits new_val)
1109
1110 #if !defined(SILC_THREADS)
1111 #define SILC_ATOMIC_CAS(bits, bp)                                       \
1112 SILC_ATOMIC_CAS_F(bits)                                                 \
1113 {                                                                       \
1114   /* No atomic operations */                                            \
1115   if (atomic->value == (SilcUInt##bits)old_val) {                       \
1116     atomic->value = new_val;                                            \
1117     return TRUE;                                                        \
1118   }                                                                     \
1119   return FALSE;                                                         \
1120 }
1121
1122 #elif defined(SILC_WIN32)
1123 #define SILC_ATOMIC_CAS(bits, bp)                                       \
1124 SILC_ATOMIC_CAS_F(bits)                                                 \
1125 {                                                                       \
1126   /* Windows */                                                         \
1127   LONG o = old_val, n = new_val;                                        \
1128   return InterlockedCompareExchange(&atomic->value, n, o) == o;         \
1129 }
1130
1131 #elif defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64))
1132 #define SILC_ATOMIC_CAS(bits, bp)                                       \
1133 SILC_ATOMIC_CAS_F(bits)                                                 \
1134 {                                                                       \
1135   /* GCC + i486 or x86_64 */                                            \
1136   SilcUInt##bits ret;                                                   \
1137   __asm __volatile(SILC_SMP_LOCK "cmpxchg" bp " %2, %1"                 \
1138                    : "=a" (ret), "=m" (atomic->value)                   \
1139                    : "r" (new_val), "m" (atomic->value), "0" (old_val)); \
1140   return ret == (SilcUInt##bits)old_val;                                \
1141 }
1142
1143 #elif defined(__GNUC__) && defined(SILC_IA64)
1144 #define SILC_ATOMIC_CAS(bits, bp)                                       \
1145 SILC_ATOMIC_CAS_F(bits)                                                 \
1146 {                                                                       \
1147   /* GCC + IA64 (GCC builtin atomic operations) */                      \
1148   SilcUInt32 o = old_val, n = new_val;                                  \
1149   return __sync_bool_compare_and_swap(&atomic->value, o, n);            \
1150 }
1151
1152 #elif defined(__GNUC__) && defined(SILC_POWERPC)
1153 #define SILC_ATOMIC_CAS(bits, bp)                                       \
1154 SILC_ATOMIC_CAS_F(bits)                                                 \
1155 {                                                                       \
1156   /* GCC + PowerPC */                                                   \
1157   /* XXX TODO */                                                        \
1158 }
1159
1160 #else /* SILC_ATOMIC_MUTEX */
1161 #define SILC_ATOMIC_CAS(bits, bp)                                       \
1162 SILC_ATOMIC_CAS_F(bits)                                                 \
1163 {                                                                       \
1164   /* Mutex */                                                           \
1165   silc_mutex_lock(atomic->lock);                                        \
1166   if (atomic->value == (SilcUInt##bits)old_val) {                       \
1167     atomic->value = new_val;                                            \
1168     silc_mutex_unlock(atomic->lock);                                    \
1169     return TRUE;                                                        \
1170   }                                                                     \
1171   silc_mutex_unlock(atomic->lock);                                      \
1172   return FALSE;                                                         \
1173 }
1174 #endif /* !SILC_THREADS */
1175
1176 SILC_ATOMIC_CAS(8, "b");
1177 SILC_ATOMIC_CAS(16, "w");
1178 SILC_ATOMIC_CAS(32, "l");
1179
1180 /****f* silcutil/SilcAtomicAPI/silc_atomic_cas_pointer
1181  *
1182  * SYNOPSIS
1183  *
1184  *    static inline
1185  *    SilcBool silc_atomic_cas_pointer(SilcAtomicPointer *atomic,
1186  *                                     void *old_ptr, void *new_ptr);
1187  *
1188  * DESCRIPTION
1189  *
1190  *    Performs compare and swap (CAS).  Atomically compares if the variable
1191  *    `atomic' has the pointer `old_ptr' and in that case swaps it with the
1192  *    pointer `new_ptr'.  Returns TRUE if the old pointer was same and it was
1193  *    swapped and FALSE if it differed and was not swapped.
1194  *
1195  ***/
1196
1197 static inline
1198 SilcBool silc_atomic_cas_pointer(SilcAtomicPointer *atomic, void *old_val,
1199                                  void *new_val)
1200 {
1201 #if !defined(SILC_THREADS)
1202   /* No atomic operations */
1203   if (atomic->value == old_val) {
1204     atomic->value = new_val;
1205     return TRUE;
1206   }
1207   return FALSE;
1208
1209 #elif defined(SILC_WIN32)
1210   /* Windows */
1211   return InterlockedCompareExchangePointer(&atomic->value, n, o) == o;
1212
1213 #elif defined(__GNUC__) && defined(SILC_I486)
1214   /* GCC + i486 */
1215   void *ret;
1216   __asm __volatile(SILC_SMP_LOCK "cmpxchgl %2, %1"
1217                    : "=a" (ret), "=m" (atomic->value)
1218                    : "c" (new_val), "m" (atomic->value), "0" (old_val));
1219   return ret == old_val;
1220
1221 #elif defined(__GNUC__) && defined(SILC_X86_64)
1222   /* GCC + x86_64 */
1223   void *ret;
1224   __asm __volatile(SILC_SMP_LOCK "cmpxchgq %q2, %1"
1225                    : "=a" (ret), "=m" (atomic->value)
1226                    : "c" (new_val), "m" (atomic->value), "0" (old_val));
1227   return ret == old_val;
1228
1229 #elif defined(__GNUC__) && defined(SILC_IA64)
1230   /* GCC + IA64 (GCC builtin atomic operations) */
1231   return  __sync_bool_compare_and_swap((long)&atomic->value, (long)old_val,
1232                                        (long)new_val);
1233
1234 #elif defined(__GNUC__) && defined(SILC_POWERPC)
1235   /* GCC + PowerPC */
1236   /* XXX TODO */
1237
1238 #else
1239   /* Mutex */
1240   silc_mutex_lock(atomic->lock);
1241   if (atomic->value == old_val) {
1242     atomic->value = new_val;
1243     silc_mutex_unlock(atomic->lock);
1244     return TRUE;
1245   }
1246   silc_mutex_unlock(atomic->lock);
1247   return FALSE;
1248 #endif
1249 }
1250
1251 #endif /* SILCATOMIC_H */