Added silcatomic.h for atomic operations.
[silc.git] / lib / silcutil / silcatomic.h
1 /*
2
3   silcatomic.h
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2006 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; version 2 of the License.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18 */
19
20 /****h* silcutil/SILC Atomic Operations Interface
21  *
22  * DESCRIPTION
23  *
24  * SILC Atomic operations interface provides utility functions to perform
25  * simple operations with integers atomically.  This enables fast integer
26  * additions and subtractions safely in multithreaded environment.  It is
27  * especially suited for reference counters and similar and is much faster
28  * than using locking.
29  *
30  * If threads were not enabled when compiling the source code, the operations
31  * are not atomic.  On some platforms this interface actually use mutual
32  * exclusion lock instead of true atomic operations, leading into some
33  * performace penalty.
34  *
35  ***/
36
37 #ifndef SILCATOMIC_H
38 #define SILCATOMIC_H
39
40 /****s* silcutil/SilcAtomicAPI/SilcAtomic
41  *
42  * NAME
43  *
44  *    typedef struct { ... } SilcAtomic;
45  *
46  * DESCRIPTION
47  *
48  *    The atomic operation structure given as argument to all atomic
49  *    operation functions.  It hols the actual atomic variable.  On most
50  *    platforms its size is 32 bits but on some platforms it may be
51  *    larger.
52  *
53  * EXAMPLE
54  *
55  *    SilcAtomic refcnt;
56  *
57  *    // Initialize atomic variable
58  *    silc_atomic_init(&refcnt, 0);
59  *
60  *    ...
61  *    // Increment referene counter
62  *    silc_atomic_add_int(&refcnt, 1);
63  *    ...
64  *
65  *    // Uninitialize atomic variable
66  *    silc_atomic_uninit(&refcnt);
67  *
68  ***/
69 #if !defined(SILC_THREADS) || defined(SILC_WIN32) || (defined(__GNUC__) &&  \
70     (defined(SILC_I486) || defined(SILC_X86_64) || defined(SILC_IA64) ||    \
71      defined(SILC_POWERPC)))
72 typedef struct
73 {
74   volatile SilcUInt32 value;
75 } SilcAtomic;
76 #else
77 #define SILC_ATOMIC_MUTEX
78 typedef struct
79 {
80   volatile SilcUInt32 value;
81   SilcMutex lock;
82 } SilcAtomic;
83 #endif
84
85 /****f* silcutil/SilcAtomicAPI/silc_atomic_init
86  *
87  * SYNOPSIS
88  *
89  *    static inline
90  *    SilcBool silc_atomic_init(SilcAtomic *atomic, int value);
91  *
92  * DESCRIPTION
93  *
94  *    Initializes the atomic variable `atomic', and sets the `value' as its
95  *    inital value.  Returns FALSE on error.  To uninitialize call the
96  *    silc_atomic_uninit function.
97  *
98  ***/
99
100 static inline
101 SilcBool silc_atomic_init(SilcAtomic *atomic, int value)
102 {
103   atomic->value = value;
104
105 #if defined(SILC_ATOMIC_MUTEX)
106   if (!silc_mutex_alloc(&atomic->lock))
107     return FALSE;
108 #endif /* SILC_ATOMIC_MUTEX */
109
110   return TRUE;
111 }
112
113 /****f* silcutil/SilcAtomicAPI/silc_atomic_uninit
114  *
115  * SYNOPSIS
116  *
117  *    static inline
118  *    void silc_atomic_uninit(SilcAtomic *atomic);
119  *
120  * DESCRIPTION
121  *
122  *    Uninitializes the atomic variable `atomic'.  This should alwyas be
123  *    called after the atomic variable is not used anymore.
124  *
125  ***/
126
127 static inline
128 void silc_atomic_uninit(SilcAtomic *atomic)
129 {
130   atomic->value = 0;
131 #if defined(SILC_ATOMIC_MUTEX)
132   silc_mutex_free(atomic->lock);
133 #endif /* SILC_ATOMIC_MUTEX */
134 }
135
136 /****f* silcutil/SilcAtomicAPI/silc_atomic_add_int
137  *
138  * SYNOPSIS
139  *
140  *    static inline
141  *    SilcUInt32 silc_atomic_add_int(SilcAtomic *atomic, int value);
142  *
143  * DESCRIPTION
144  *
145  *    Atomically adds `value' to 32-bit integer.  Returns the value after
146  *    addition.
147  *
148  ***/
149
150 static inline
151 SilcUInt32 silc_atomic_add_int(SilcAtomic *atomic, int value)
152 {
153   SilcUInt32 ret;
154
155 #if !defined(SILC_THREADS)
156   /* No atomic operations */
157   ret = atomic->value;
158   atomic->value += value;
159
160 #elif defined(SILC_WIN32)
161   /* Windows */
162   ret = InterlockedExchangeAdd(&atomic->value, (LONG)value);
163
164 #elif defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64))
165   /* GCC + i486 or x86_64 */
166   __asm __volatile("lock; xaddl %0, %1"
167                    : "=r" (ret), "+m" (atomic->value)
168                    : "0" (value));
169
170 #elif defined(__GNUC__) && defined(SILC_IA64)
171   /* GCC + IA64 (GCC builtin atomic operations) */
172   ret = __sync_fetch_and_add(&atomic->value, value);
173
174 #elif defined(__GNUC__) && defined(SILC_POWERPC)
175   /* GCC + PowerPC (code adapted from IBM's documentation) */
176   /* XXX Hmm.. should I sync and isync this?... */
177   __asm __volatile("0: lwarx  %0,  0, %2\n"
178                    "   add    %0, %1, %0\n"
179                    "   stwcx. %0,  0, %2\n"
180                    "   bne-   0b"
181                    : "=&r" (ret)
182                    : "r" (value), "r" (&atomic->value)
183                    : "cc");
184   return ret;
185
186 #else
187   /* Mutex */
188   silc_mutex_lock(atomic->lock);
189   ret = atomic->value;
190   atomic->value += value;
191   silc_mutex_unlock(atomic->lock);
192 #endif
193
194   return ret + value;
195 }
196
197 /****f* silcutil/SilcAtomicAPI/silc_atomic_sub_int
198  *
199  * SYNOPSIS
200  *
201  *    static inline
202  *    SilcUInt32 silc_atomic_sub_int(SilcAtomic *atomic, int value);
203  *
204  * DESCRIPTION
205  *
206  *    Atomically subtracts `value' from 32-bit integer.  Returns the value
207  *    after subtraction.
208  *
209  ***/
210
211 static inline
212 SilcUInt32 silc_atomic_sub_int(SilcAtomic *atomic, int value)
213 {
214   return silc_atomic_add_int(atomic, -value);
215 }
216
217 /****f* silcutil/SilcAtomicAPI/silc_atomic_get_int
218  *
219  * SYNOPSIS
220  *
221  *    static inline
222  *    SilcUInt32 silc_atomic_get_int(SilcAtomic *atomic);
223  *
224  * DESCRIPTION
225  *
226  *    Returns the current value of the atomic variable.
227  *
228  ***/
229
230 static inline
231 SilcUInt32 silc_atomic_get_int(SilcAtomic *atomic)
232 {
233   SilcUInt32 ret;
234
235 #if !defined(SILC_THREADS) || defined(SILC_WIN32) ||                     \
236      (defined(__GNUC__) && (defined(SILC_I486) || defined(SILC_X86_64)))
237   /* No threads, Windows, i486 or x86_64, no memory barrier needed */
238   ret = atomic->value;
239   return ret;
240
241 #elif defined(__GNUC__) && defined(SILC_IA64)
242   /* IA64, memory barrier needed */
243   __sync_synchronize();
244   ret = atomic->value;
245   return ret;
246
247 #elif defined(__GNUC__) && defined(SILC_POWERPC)
248   /* PowerPC, memory barrier needed */
249   __asm("sync" : : : "memory");
250   ret = atomic->value;
251   return ret;
252
253 #else
254   /* Mutex */
255   silc_mutex_lock(atomic->lock);
256   ret = atomic->value;
257   silc_mutex_unlock(atomic->lock);
258   return ret;
259 #endif
260 }
261
262 #endif /* SILCATOMIC_H */