Merge pull request #790 from akoeplinger/fixtests
[mono.git] / mono / utils / atomic.h
1 /*
2  * atomic.h:  Atomic operations
3  *
4  * Author:
5  *      Dick Porter (dick@ximian.com)
6  *
7  * (C) 2002 Ximian, Inc.
8  * Copyright 2012 Xamarin Inc
9  */
10
11 #ifndef _WAPI_ATOMIC_H_
12 #define _WAPI_ATOMIC_H_
13
14 #include "config.h"
15 #include <glib.h>
16
17 #ifdef ENABLE_EXTENSION_MODULE
18 #include "../../../mono-extensions/mono/utils/atomic.h"
19 #endif
20
21 /* On Windows, we always use the functions provided by the Windows API. */
22 #if defined(__WIN32__) || defined(_WIN32)
23
24 #include <windows.h>
25 #include <mono/utils/mono-membar.h>
26
27 /* mingw is missing InterlockedCompareExchange64 () from winbase.h */
28 #if HAVE_DECL_INTERLOCKEDCOMPAREEXCHANGE64==0
29 static inline gint64 InterlockedCompareExchange64(volatile gint64 *dest, gint64 exch, gint64 comp)
30 {
31         return __sync_val_compare_and_swap (dest, comp, exch);
32 }
33 #endif
34
35 /* mingw is missing InterlockedExchange64 () from winbase.h */
36 #if HAVE_DECL_INTERLOCKEDEXCHANGE64==0
37 static inline gint64 InterlockedExchange64(volatile gint64 *val, gint64 new_val)
38 {
39         gint64 old_val;
40         do {
41                 old_val = *val;
42         } while (InterlockedCompareExchange64 (val, new_val, old_val) != old_val);
43         return old_val;
44 }
45 #endif
46
47 /* mingw is missing InterlockedIncrement64 () from winbase.h */
48 #if HAVE_DECL_INTERLOCKEDINCREMENT64==0
49 static inline gint64 InterlockedIncrement64(volatile gint64 *val)
50 {
51         return __sync_add_and_fetch (val, 1);
52 }
53 #endif
54
55 /* mingw is missing InterlockedDecrement64 () from winbase.h */
56 #if HAVE_DECL_INTERLOCKEDDECREMENT64==0
57 static inline gint64 InterlockedDecrement64(volatile gint64 *val)
58 {
59         return __sync_sub_and_fetch (val, 1);
60 }
61 #endif
62
63 /* mingw is missing InterlockedAdd () from winbase.h */
64 #if HAVE_DECL_INTERLOCKEDADD==0
65 static inline gint32 InterlockedAdd(volatile gint32 *dest, gint32 add)
66 {
67         return __sync_add_and_fetch (dest, add);
68 }
69 #endif
70
71 /* mingw is missing InterlockedAdd64 () from winbase.h */
72 #if HAVE_DECL_INTERLOCKEDADD64==0
73 static inline gint64 InterlockedAdd64(volatile gint64 *dest, gint64 add)
74 {
75         return __sync_add_and_fetch (dest, add);
76 }
77 #endif
78
79 /* And now for some dirty hacks... The Windows API doesn't
80  * provide any useful primitives for this (other than getting
81  * into architecture-specific madness), so use CAS. */
82
83 static inline gint32 InterlockedRead(volatile gint32 *src)
84 {
85         return InterlockedCompareExchange (src, 0, 0);
86 }
87
88 static inline gint64 InterlockedRead64(volatile gint64 *src)
89 {
90         return InterlockedCompareExchange64 (src, 0, 0);
91 }
92
93 static inline gpointer InterlockedReadPointer(volatile gpointer *src)
94 {
95         return InterlockedCompareExchangePointer (src, NULL, NULL);
96 }
97
98 static inline void InterlockedWrite(volatile gint32 *dst, gint32 val)
99 {
100         InterlockedExchange (dst, val);
101 }
102
103 static inline void InterlockedWrite64(volatile gint64 *dst, gint64 val)
104 {
105         InterlockedExchange64 (dst, val);
106 }
107
108 static inline void InterlockedWritePointer(volatile gpointer *dst, gpointer val)
109 {
110         InterlockedExchangePointer (dst, val);
111 }
112
113 /* We can't even use CAS for these, so write them out
114  * explicitly according to x86(_64) semantics... */
115
116 static inline gint8 InterlockedRead8(volatile gint8 *src)
117 {
118         return *src;
119 }
120
121 static inline gint16 InterlockedRead16(volatile gint16 *src)
122 {
123         return *src;
124 }
125
126 static inline void InterlockedWrite8(volatile gint8 *dst, gint8 val)
127 {
128         *dst = val;
129         mono_memory_barrier ();
130 }
131
132 static inline void InterlockedWrite16(volatile gint16 *dst, gint16 val)
133 {
134         *dst = val;
135         mono_memory_barrier ();
136 }
137
138 /* Prefer GCC atomic ops if the target supports it (see configure.in). */
139 #elif defined(USE_GCC_ATOMIC_OPS)
140
141 static inline gint32 InterlockedCompareExchange(volatile gint32 *dest,
142                                                 gint32 exch, gint32 comp)
143 {
144         return __sync_val_compare_and_swap (dest, comp, exch);
145 }
146
147 static inline gpointer InterlockedCompareExchangePointer(volatile gpointer *dest, gpointer exch, gpointer comp)
148 {
149         return __sync_val_compare_and_swap (dest, comp, exch);
150 }
151
152 static inline gint32 InterlockedAdd(volatile gint32 *dest, gint32 add)
153 {
154         return __sync_add_and_fetch (dest, add);
155 }
156
157 static inline gint32 InterlockedIncrement(volatile gint32 *val)
158 {
159         return __sync_add_and_fetch (val, 1);
160 }
161
162 static inline gint32 InterlockedDecrement(volatile gint32 *val)
163 {
164         return __sync_sub_and_fetch (val, 1);
165 }
166
167 static inline gint32 InterlockedExchange(volatile gint32 *val, gint32 new_val)
168 {
169         gint32 old_val;
170         do {
171                 old_val = *val;
172         } while (__sync_val_compare_and_swap (val, old_val, new_val) != old_val);
173         return old_val;
174 }
175
176 static inline gpointer InterlockedExchangePointer(volatile gpointer *val,
177                                                   gpointer new_val)
178 {
179         gpointer old_val;
180         do {
181                 old_val = *val;
182         } while (__sync_val_compare_and_swap (val, old_val, new_val) != old_val);
183         return old_val;
184 }
185
186 static inline gint32 InterlockedExchangeAdd(volatile gint32 *val, gint32 add)
187 {
188         return __sync_fetch_and_add (val, add);
189 }
190
191 static inline gint8 InterlockedRead8(volatile gint8 *src)
192 {
193         /* Kind of a hack, but GCC doesn't give us anything better, and it's
194          * certainly not as bad as using a CAS loop. */
195         return __sync_fetch_and_add (src, 0);
196 }
197
198 static inline gint16 InterlockedRead16(volatile gint16 *src)
199 {
200         return __sync_fetch_and_add (src, 0);
201 }
202
203 static inline gint32 InterlockedRead(volatile gint32 *src)
204 {
205         return __sync_fetch_and_add (src, 0);
206 }
207
208 static inline void InterlockedWrite8(volatile gint8 *dst, gint8 val)
209 {
210         /* Nothing useful from GCC at all, so fall back to CAS. */
211         gint8 old_val;
212         do {
213                 old_val = *dst;
214         } while (__sync_val_compare_and_swap (dst, old_val, val) != old_val);
215 }
216
217 static inline void InterlockedWrite16(volatile gint16 *dst, gint16 val)
218 {
219         gint16 old_val;
220         do {
221                 old_val = *dst;
222         } while (__sync_val_compare_and_swap (dst, old_val, val) != old_val);
223 }
224
225 static inline void InterlockedWrite(volatile gint32 *dst, gint32 val)
226 {
227         /* Nothing useful from GCC at all, so fall back to CAS. */
228         gint32 old_val;
229         do {
230                 old_val = *dst;
231         } while (__sync_val_compare_and_swap (dst, old_val, val) != old_val);
232 }
233
234 #if defined (TARGET_OSX) || defined (__arm__) || (defined (__mips__) && !defined (__mips64)) || (defined (__powerpc__) && !defined (__powerpc64__)) || (defined (__sparc__) && !defined (__arch64__))
235 #define BROKEN_64BIT_ATOMICS_INTRINSIC 1
236 #endif
237
238 #if !defined (BROKEN_64BIT_ATOMICS_INTRINSIC)
239
240 static inline gint64 InterlockedCompareExchange64(volatile gint64 *dest, gint64 exch, gint64 comp)
241 {
242         return __sync_val_compare_and_swap (dest, comp, exch);
243 }
244
245 static inline gint64 InterlockedAdd64(volatile gint64 *dest, gint64 add)
246 {
247         return __sync_add_and_fetch (dest, add);
248 }
249
250 static inline gint64 InterlockedIncrement64(volatile gint64 *val)
251 {
252         return __sync_add_and_fetch (val, 1);
253 }
254
255 static inline gint64 InterlockedDecrement64(volatile gint64 *val)
256 {
257         return __sync_sub_and_fetch (val, 1);
258 }
259
260 static inline gint64 InterlockedExchangeAdd64(volatile gint64 *val, gint64 add)
261 {
262         return __sync_fetch_and_add (val, add);
263 }
264
265 static inline gint64 InterlockedRead64(volatile gint64 *src)
266 {
267         /* Kind of a hack, but GCC doesn't give us anything better. */
268         return __sync_fetch_and_add (src, 0);
269 }
270
271 #else
272
273 /* Implement 64-bit cmpxchg by hand or emulate it. */
274 extern gint64 InterlockedCompareExchange64(volatile gint64 *dest, gint64 exch, gint64 comp);
275
276 /* Implement all other 64-bit atomics in terms of a specialized CAS
277  * in this case, since chances are that the other 64-bit atomic
278  * intrinsics are broken too.
279  */
280
281 static inline gint64 InterlockedExchangeAdd64(volatile gint64 *dest, gint64 add)
282 {
283         gint64 old_val;
284         do {
285                 old_val = *dest;
286         } while (InterlockedCompareExchange64 (dest, old_val + add, old_val) != old_val);
287         return old_val;
288 }
289
290 static inline gint64 InterlockedIncrement64(volatile gint64 *val)
291 {
292         gint64 get, set;
293         do {
294                 get = *val;
295                 set = get + 1;
296         } while (InterlockedCompareExchange64 (val, set, get) != get);
297         return set;
298 }
299
300 static inline gint64 InterlockedDecrement64(volatile gint64 *val)
301 {
302         gint64 get, set;
303         do {
304                 get = *val;
305                 set = get - 1;
306         } while (InterlockedCompareExchange64 (val, set, get) != get);
307         return set;
308 }
309
310 static inline gint64 InterlockedAdd64(volatile gint64 *dest, gint64 add)
311 {
312         gint64 get, set;
313         do {
314                 get = *dest;
315                 set = get + add;
316         } while (InterlockedCompareExchange64 (dest, set, get) != get);
317         return set;
318 }
319
320 static inline gint64 InterlockedRead64(volatile gint64 *src)
321 {
322         return InterlockedCompareExchange64 (src, 0, 0);
323 }
324
325 #endif
326
327 static inline gpointer InterlockedReadPointer(volatile gpointer *src)
328 {
329         return InterlockedCompareExchangePointer (src, NULL, NULL);
330 }
331
332 static inline void InterlockedWritePointer(volatile gpointer *dst, gpointer val)
333 {
334         InterlockedExchangePointer (dst, val);
335 }
336
337 /* We always implement this in terms of a 64-bit cmpxchg since
338  * GCC doesn't have an intrisic to model it anyway. */
339 static inline gint64 InterlockedExchange64(volatile gint64 *val, gint64 new_val)
340 {
341         gint64 old_val;
342         do {
343                 old_val = *val;
344         } while (InterlockedCompareExchange64 (val, new_val, old_val) != old_val);
345         return old_val;
346 }
347
348 static inline void InterlockedWrite64(volatile gint64 *dst, gint64 val)
349 {
350         /* Nothing useful from GCC at all, so fall back to CAS. */
351         InterlockedExchange64 (dst, val);
352 }
353
354 #elif defined(__ia64__)
355
356 #ifdef __INTEL_COMPILER
357 #include <ia64intrin.h>
358 #endif
359
360 static inline gint32 InterlockedCompareExchange(gint32 volatile *dest,
361                                                 gint32 exch, gint32 comp)
362 {
363         gint32 old;
364         guint64 real_comp;
365
366 #ifdef __INTEL_COMPILER
367         old = _InterlockedCompareExchange (dest, exch, comp);
368 #else
369         /* cmpxchg4 zero extends the value read from memory */
370         real_comp = (guint64)(guint32)comp;
371         asm volatile ("mov ar.ccv = %2 ;;\n\t"
372                                   "cmpxchg4.acq %0 = [%1], %3, ar.ccv\n\t"
373                                   : "=r" (old) : "r" (dest), "r" (real_comp), "r" (exch));
374 #endif
375
376         return(old);
377 }
378
379 static inline gpointer InterlockedCompareExchangePointer(gpointer volatile *dest,
380                                                 gpointer exch, gpointer comp)
381 {
382         gpointer old;
383
384 #ifdef __INTEL_COMPILER
385         old = _InterlockedCompareExchangePointer (dest, exch, comp);
386 #else
387         asm volatile ("mov ar.ccv = %2 ;;\n\t"
388                                   "cmpxchg8.acq %0 = [%1], %3, ar.ccv\n\t"
389                                   : "=r" (old) : "r" (dest), "r" (comp), "r" (exch));
390 #endif
391
392         return(old);
393 }
394
395 static inline gint32 InterlockedIncrement(gint32 volatile *val)
396 {
397 #ifdef __INTEL_COMPILER
398         return _InterlockedIncrement (val);
399 #else
400         gint32 old;
401
402         do {
403                 old = *val;
404         } while (InterlockedCompareExchange (val, old + 1, old) != old);
405
406         return old + 1;
407 #endif
408 }
409
410 static inline gint32 InterlockedDecrement(gint32 volatile *val)
411 {
412 #ifdef __INTEL_COMPILER
413         return _InterlockedDecrement (val);
414 #else
415         gint32 old;
416
417         do {
418                 old = *val;
419         } while (InterlockedCompareExchange (val, old - 1, old) != old);
420
421         return old - 1;
422 #endif
423 }
424
425 static inline gint32 InterlockedExchange(gint32 volatile *dest, gint32 new_val)
426 {
427 #ifdef __INTEL_COMPILER
428         return _InterlockedExchange (dest, new_val);
429 #else
430         gint32 res;
431
432         do {
433                 res = *dest;
434         } while (InterlockedCompareExchange (dest, new_val, res) != res);
435
436         return res;
437 #endif
438 }
439
440 static inline gpointer InterlockedExchangePointer(gpointer volatile *dest, gpointer new_val)
441 {
442 #ifdef __INTEL_COMPILER
443         return (gpointer)_InterlockedExchange64 ((gint64*)dest, (gint64)new_val);
444 #else
445         gpointer res;
446
447         do {
448                 res = *dest;
449         } while (InterlockedCompareExchangePointer (dest, new_val, res) != res);
450
451         return res;
452 #endif
453 }
454
455 static inline gint32 InterlockedExchangeAdd(gint32 volatile *val, gint32 add)
456 {
457         gint32 old;
458
459 #ifdef __INTEL_COMPILER
460         old = _InterlockedExchangeAdd (val, add);
461 #else
462         do {
463                 old = *val;
464         } while (InterlockedCompareExchange (val, old + add, old) != old);
465
466         return old;
467 #endif
468 }
469
470 #else
471
472 #define WAPI_NO_ATOMIC_ASM
473
474 extern gint32 InterlockedCompareExchange(volatile gint32 *dest, gint32 exch, gint32 comp);
475 extern gint64 InterlockedCompareExchange64(volatile gint64 *dest, gint64 exch, gint64 comp);
476 extern gpointer InterlockedCompareExchangePointer(volatile gpointer *dest, gpointer exch, gpointer comp);
477 extern gint32 InterlockedAdd(volatile gint32 *dest, gint32 add);
478 extern gint64 InterlockedAdd64(volatile gint64 *dest, gint64 add);
479 extern gint32 InterlockedIncrement(volatile gint32 *dest);
480 extern gint64 InterlockedIncrement64(volatile gint64 *dest);
481 extern gint32 InterlockedDecrement(volatile gint32 *dest);
482 extern gint64 InterlockedDecrement64(volatile gint64 *dest);
483 extern gint32 InterlockedExchange(volatile gint32 *dest, gint32 exch);
484 extern gint64 InterlockedExchange64(volatile gint64 *dest, gint64 exch);
485 extern gpointer InterlockedExchangePointer(volatile gpointer *dest, gpointer exch);
486 extern gint32 InterlockedExchangeAdd(volatile gint32 *dest, gint32 add);
487 extern gint64 InterlockedExchangeAdd64(volatile gint64 *dest, gint64 add);
488 extern gint8 InterlockedRead8(volatile gint8 *src);
489 extern gint16 InterlockedRead16(volatile gint16 *src);
490 extern gint32 InterlockedRead(volatile gint32 *src);
491 extern gint64 InterlockedRead64(volatile gint64 *src);
492 extern gpointer InterlockedReadPointer(volatile gpointer *src);
493 extern void InterlockedWrite8(volatile gint8 *dst, gint8 val);
494 extern void InterlockedWrite16(volatile gint16 *dst, gint16 val);
495 extern void InterlockedWrite(volatile gint32 *dst, gint32 val);
496 extern void InterlockedWrite64(volatile gint64 *dst, gint64 val);
497 extern void InterlockedWritePointer(volatile gpointer *dst, gpointer val);
498
499 #endif
500
501 #endif /* _WAPI_ATOMIC_H_ */