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