2 * atomic.h: Atomic operations
5 * Dick Porter (dick@ximian.com)
7 * (C) 2002 Ximian, Inc.
8 * Copyright 2012 Xamarin Inc
9 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
12 #ifndef _WAPI_ATOMIC_H_
13 #define _WAPI_ATOMIC_H_
19 The current Nexus 7 arm-v7a fails with:
20 F/MonoDroid( 1568): shared runtime initialization error: Cannot load library: reloc_library[1285]: 37 cannot locate '__sync_val_compare_and_swap_8'
22 Apple targets have historically being problematic, xcode 4.6 would miscompile the intrinsic.
25 /* On Windows, we always use the functions provided by the Windows API. */
26 #if defined(__WIN32__) || defined(_WIN32)
28 #ifndef WIN32_LEAN_AND_MEAN
29 #define WIN32_LEAN_AND_MEAN
32 #include <mono/utils/mono-membar.h>
34 /* mingw is missing InterlockedCompareExchange64 () from winbase.h */
35 #if HAVE_DECL_INTERLOCKEDCOMPAREEXCHANGE64==0
36 static inline gint64 InterlockedCompareExchange64(volatile gint64 *dest, gint64 exch, gint64 comp)
38 return __sync_val_compare_and_swap (dest, comp, exch);
42 /* mingw is missing InterlockedExchange64 () from winbase.h */
43 #if HAVE_DECL_INTERLOCKEDEXCHANGE64==0
44 static inline gint64 InterlockedExchange64(volatile gint64 *val, gint64 new_val)
49 } while (InterlockedCompareExchange64 (val, new_val, old_val) != old_val);
54 /* mingw is missing InterlockedIncrement64 () from winbase.h */
55 #if HAVE_DECL_INTERLOCKEDINCREMENT64==0
56 static inline gint64 InterlockedIncrement64(volatile gint64 *val)
58 return __sync_add_and_fetch (val, 1);
62 /* mingw is missing InterlockedDecrement64 () from winbase.h */
63 #if HAVE_DECL_INTERLOCKEDDECREMENT64==0
64 static inline gint64 InterlockedDecrement64(volatile gint64 *val)
66 return __sync_sub_and_fetch (val, 1);
70 /* mingw is missing InterlockedAdd () from winbase.h */
71 #if HAVE_DECL_INTERLOCKEDADD==0
72 static inline gint32 InterlockedAdd(volatile gint32 *dest, gint32 add)
74 return __sync_add_and_fetch (dest, add);
78 /* mingw is missing InterlockedAdd64 () from winbase.h */
79 #if HAVE_DECL_INTERLOCKEDADD64==0
80 static inline gint64 InterlockedAdd64(volatile gint64 *dest, gint64 add)
82 return __sync_add_and_fetch (dest, add);
86 #if defined(_MSC_VER) && !defined(InterlockedAdd)
87 /* MSVC before 2013 only defines InterlockedAdd* for the Itanium architecture */
88 static inline gint32 InterlockedAdd(volatile gint32 *dest, gint32 add)
90 return InterlockedExchangeAdd (dest, add) + add;
94 #if defined(_MSC_VER) && !defined(InterlockedAdd64)
95 #if defined(InterlockedExchangeAdd64)
96 /* This may be defined only on amd64 */
97 static inline gint64 InterlockedAdd64(volatile gint64 *dest, gint64 add)
99 return InterlockedExchangeAdd64 (dest, add) + add;
102 static inline gint64 InterlockedAdd64(volatile gint64 *dest, gint64 add)
108 } while (prev_value != InterlockedCompareExchange64(dest, prev_value + add, prev_value));
110 return prev_value + add;
116 #define TO_INTERLOCKED_ARGP(ptr) ((volatile LONG*)(ptr))
118 #define TO_INTERLOCKED_ARGP(ptr) (ptr)
121 /* And now for some dirty hacks... The Windows API doesn't
122 * provide any useful primitives for this (other than getting
123 * into architecture-specific madness), so use CAS. */
125 static inline gint32 InterlockedRead(volatile gint32 *src)
127 return InterlockedCompareExchange (TO_INTERLOCKED_ARGP (src), 0, 0);
130 static inline gint64 InterlockedRead64(volatile gint64 *src)
132 return InterlockedCompareExchange64 (src, 0, 0);
135 static inline gpointer InterlockedReadPointer(volatile gpointer *src)
137 return InterlockedCompareExchangePointer (src, NULL, NULL);
140 static inline void InterlockedWrite(volatile gint32 *dst, gint32 val)
142 InterlockedExchange (TO_INTERLOCKED_ARGP (dst), val);
145 static inline void InterlockedWrite64(volatile gint64 *dst, gint64 val)
147 InterlockedExchange64 (dst, val);
150 static inline void InterlockedWritePointer(volatile gpointer *dst, gpointer val)
152 InterlockedExchangePointer (dst, val);
155 /* We can't even use CAS for these, so write them out
156 * explicitly according to x86(_64) semantics... */
158 static inline gint8 InterlockedRead8(volatile gint8 *src)
163 static inline gint16 InterlockedRead16(volatile gint16 *src)
168 static inline void InterlockedWrite8(volatile gint8 *dst, gint8 val)
171 mono_memory_barrier ();
174 static inline void InterlockedWrite16(volatile gint16 *dst, gint16 val)
177 mono_memory_barrier ();
180 /* Prefer GCC atomic ops if the target supports it (see configure.ac). */
181 #elif defined(USE_GCC_ATOMIC_OPS)
183 static inline gint32 InterlockedCompareExchange(volatile gint32 *dest,
184 gint32 exch, gint32 comp)
186 return __sync_val_compare_and_swap (dest, comp, exch);
189 static inline gpointer InterlockedCompareExchangePointer(volatile gpointer *dest, gpointer exch, gpointer comp)
191 return __sync_val_compare_and_swap (dest, comp, exch);
194 static inline gint32 InterlockedAdd(volatile gint32 *dest, gint32 add)
196 return __sync_add_and_fetch (dest, add);
199 static inline gint32 InterlockedIncrement(volatile gint32 *val)
201 return __sync_add_and_fetch (val, 1);
204 static inline gint32 InterlockedDecrement(volatile gint32 *val)
206 return __sync_sub_and_fetch (val, 1);
209 static inline gint32 InterlockedExchange(volatile gint32 *val, gint32 new_val)
214 } while (__sync_val_compare_and_swap (val, old_val, new_val) != old_val);
218 static inline gpointer InterlockedExchangePointer(volatile gpointer *val,
224 } while (__sync_val_compare_and_swap (val, old_val, new_val) != old_val);
228 static inline gint32 InterlockedExchangeAdd(volatile gint32 *val, gint32 add)
230 return __sync_fetch_and_add (val, add);
233 static inline gint8 InterlockedRead8(volatile gint8 *src)
235 /* Kind of a hack, but GCC doesn't give us anything better, and it's
236 * certainly not as bad as using a CAS loop. */
237 return __sync_fetch_and_add (src, 0);
240 static inline gint16 InterlockedRead16(volatile gint16 *src)
242 return __sync_fetch_and_add (src, 0);
245 static inline gint32 InterlockedRead(volatile gint32 *src)
247 return __sync_fetch_and_add (src, 0);
250 static inline void InterlockedWrite8(volatile gint8 *dst, gint8 val)
252 /* Nothing useful from GCC at all, so fall back to CAS. */
256 } while (__sync_val_compare_and_swap (dst, old_val, val) != old_val);
259 static inline void InterlockedWrite16(volatile gint16 *dst, gint16 val)
264 } while (__sync_val_compare_and_swap (dst, old_val, val) != old_val);
267 static inline void InterlockedWrite(volatile gint32 *dst, gint32 val)
269 /* Nothing useful from GCC at all, so fall back to CAS. */
273 } while (__sync_val_compare_and_swap (dst, old_val, val) != old_val);
276 #if defined (TARGET_OSX) || defined (__arm__) || (defined (__mips__) && !defined (__mips64)) || (defined (__powerpc__) && !defined (__powerpc64__)) || (defined (__sparc__) && !defined (__arch64__))
277 #define BROKEN_64BIT_ATOMICS_INTRINSIC 1
280 #if !defined (BROKEN_64BIT_ATOMICS_INTRINSIC)
282 static inline gint64 InterlockedCompareExchange64(volatile gint64 *dest, gint64 exch, gint64 comp)
284 return __sync_val_compare_and_swap (dest, comp, exch);
287 static inline gint64 InterlockedAdd64(volatile gint64 *dest, gint64 add)
289 return __sync_add_and_fetch (dest, add);
292 static inline gint64 InterlockedIncrement64(volatile gint64 *val)
294 return __sync_add_and_fetch (val, 1);
297 static inline gint64 InterlockedDecrement64(volatile gint64 *val)
299 return __sync_sub_and_fetch (val, 1);
302 static inline gint64 InterlockedExchangeAdd64(volatile gint64 *val, gint64 add)
304 return __sync_fetch_and_add (val, add);
307 static inline gint64 InterlockedRead64(volatile gint64 *src)
309 /* Kind of a hack, but GCC doesn't give us anything better. */
310 return __sync_fetch_and_add (src, 0);
315 /* Implement 64-bit cmpxchg by hand or emulate it. */
316 extern gint64 InterlockedCompareExchange64(volatile gint64 *dest, gint64 exch, gint64 comp);
318 /* Implement all other 64-bit atomics in terms of a specialized CAS
319 * in this case, since chances are that the other 64-bit atomic
320 * intrinsics are broken too.
323 static inline gint64 InterlockedExchangeAdd64(volatile gint64 *dest, gint64 add)
328 } while (InterlockedCompareExchange64 (dest, old_val + add, old_val) != old_val);
332 static inline gint64 InterlockedIncrement64(volatile gint64 *val)
338 } while (InterlockedCompareExchange64 (val, set, get) != get);
342 static inline gint64 InterlockedDecrement64(volatile gint64 *val)
348 } while (InterlockedCompareExchange64 (val, set, get) != get);
352 static inline gint64 InterlockedAdd64(volatile gint64 *dest, gint64 add)
358 } while (InterlockedCompareExchange64 (dest, set, get) != get);
362 static inline gint64 InterlockedRead64(volatile gint64 *src)
364 return InterlockedCompareExchange64 (src, 0, 0);
369 static inline gpointer InterlockedReadPointer(volatile gpointer *src)
371 return InterlockedCompareExchangePointer (src, NULL, NULL);
374 static inline void InterlockedWritePointer(volatile gpointer *dst, gpointer val)
376 InterlockedExchangePointer (dst, val);
379 /* We always implement this in terms of a 64-bit cmpxchg since
380 * GCC doesn't have an intrisic to model it anyway. */
381 static inline gint64 InterlockedExchange64(volatile gint64 *val, gint64 new_val)
386 } while (InterlockedCompareExchange64 (val, new_val, old_val) != old_val);
390 static inline void InterlockedWrite64(volatile gint64 *dst, gint64 val)
392 /* Nothing useful from GCC at all, so fall back to CAS. */
393 InterlockedExchange64 (dst, val);
396 #elif defined(__ia64__)
398 #ifdef __INTEL_COMPILER
399 #include <ia64intrin.h>
402 static inline gint32 InterlockedCompareExchange(gint32 volatile *dest,
403 gint32 exch, gint32 comp)
408 #ifdef __INTEL_COMPILER
409 old = _InterlockedCompareExchange (dest, exch, comp);
411 /* cmpxchg4 zero extends the value read from memory */
412 real_comp = (guint64)(guint32)comp;
413 asm volatile ("mov ar.ccv = %2 ;;\n\t"
414 "cmpxchg4.acq %0 = [%1], %3, ar.ccv\n\t"
415 : "=r" (old) : "r" (dest), "r" (real_comp), "r" (exch));
421 static inline gpointer InterlockedCompareExchangePointer(gpointer volatile *dest,
422 gpointer exch, gpointer comp)
426 #ifdef __INTEL_COMPILER
427 old = _InterlockedCompareExchangePointer (dest, exch, comp);
429 asm volatile ("mov ar.ccv = %2 ;;\n\t"
430 "cmpxchg8.acq %0 = [%1], %3, ar.ccv\n\t"
431 : "=r" (old) : "r" (dest), "r" (comp), "r" (exch));
437 static inline gint32 InterlockedIncrement(gint32 volatile *val)
439 #ifdef __INTEL_COMPILER
440 return _InterlockedIncrement (val);
446 } while (InterlockedCompareExchange (val, old + 1, old) != old);
452 static inline gint32 InterlockedDecrement(gint32 volatile *val)
454 #ifdef __INTEL_COMPILER
455 return _InterlockedDecrement (val);
461 } while (InterlockedCompareExchange (val, old - 1, old) != old);
467 static inline gint32 InterlockedExchange(gint32 volatile *dest, gint32 new_val)
469 #ifdef __INTEL_COMPILER
470 return _InterlockedExchange (dest, new_val);
476 } while (InterlockedCompareExchange (dest, new_val, res) != res);
482 static inline gpointer InterlockedExchangePointer(gpointer volatile *dest, gpointer new_val)
484 #ifdef __INTEL_COMPILER
485 return (gpointer)_InterlockedExchange64 ((gint64*)dest, (gint64)new_val);
491 } while (InterlockedCompareExchangePointer (dest, new_val, res) != res);
497 static inline gint32 InterlockedExchangeAdd(gint32 volatile *val, gint32 add)
501 #ifdef __INTEL_COMPILER
502 old = _InterlockedExchangeAdd (val, add);
506 } while (InterlockedCompareExchange (val, old + add, old) != old);
514 #define WAPI_NO_ATOMIC_ASM
516 extern gint32 InterlockedCompareExchange(volatile gint32 *dest, gint32 exch, gint32 comp);
517 extern gint64 InterlockedCompareExchange64(volatile gint64 *dest, gint64 exch, gint64 comp);
518 extern gpointer InterlockedCompareExchangePointer(volatile gpointer *dest, gpointer exch, gpointer comp);
519 extern gint32 InterlockedAdd(volatile gint32 *dest, gint32 add);
520 extern gint64 InterlockedAdd64(volatile gint64 *dest, gint64 add);
521 extern gint32 InterlockedIncrement(volatile gint32 *dest);
522 extern gint64 InterlockedIncrement64(volatile gint64 *dest);
523 extern gint32 InterlockedDecrement(volatile gint32 *dest);
524 extern gint64 InterlockedDecrement64(volatile gint64 *dest);
525 extern gint32 InterlockedExchange(volatile gint32 *dest, gint32 exch);
526 extern gint64 InterlockedExchange64(volatile gint64 *dest, gint64 exch);
527 extern gpointer InterlockedExchangePointer(volatile gpointer *dest, gpointer exch);
528 extern gint32 InterlockedExchangeAdd(volatile gint32 *dest, gint32 add);
529 extern gint64 InterlockedExchangeAdd64(volatile gint64 *dest, gint64 add);
530 extern gint8 InterlockedRead8(volatile gint8 *src);
531 extern gint16 InterlockedRead16(volatile gint16 *src);
532 extern gint32 InterlockedRead(volatile gint32 *src);
533 extern gint64 InterlockedRead64(volatile gint64 *src);
534 extern gpointer InterlockedReadPointer(volatile gpointer *src);
535 extern void InterlockedWrite8(volatile gint8 *dst, gint8 val);
536 extern void InterlockedWrite16(volatile gint16 *dst, gint16 val);
537 extern void InterlockedWrite(volatile gint32 *dst, gint32 val);
538 extern void InterlockedWrite64(volatile gint64 *dst, gint64 val);
539 extern void InterlockedWritePointer(volatile gpointer *dst, gpointer val);
543 #endif /* _WAPI_ATOMIC_H_ */