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