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