Merge pull request #2545 from ermshiperete/Xamarin-24974
[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  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
10  */
11
12 #ifndef _WAPI_ATOMIC_H_
13 #define _WAPI_ATOMIC_H_
14
15 #include "config.h"
16 #include <glib.h>
17
18 /*
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'
21
22 Apple targets have historically being problematic, xcode 4.6 would miscompile the intrinsic.
23 */
24
25 /* On Windows, we always use the functions provided by the Windows API. */
26 #if defined(__WIN32__) || defined(_WIN32)
27
28 #ifndef WIN32_LEAN_AND_MEAN
29 #define WIN32_LEAN_AND_MEAN
30 #endif
31 #include <windows.h>
32 #include <mono/utils/mono-membar.h>
33
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)
37 {
38         return __sync_val_compare_and_swap (dest, comp, exch);
39 }
40 #endif
41
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)
45 {
46         gint64 old_val;
47         do {
48                 old_val = *val;
49         } while (InterlockedCompareExchange64 (val, new_val, old_val) != old_val);
50         return old_val;
51 }
52 #endif
53
54 /* mingw is missing InterlockedIncrement64 () from winbase.h */
55 #if HAVE_DECL_INTERLOCKEDINCREMENT64==0
56 static inline gint64 InterlockedIncrement64(volatile gint64 *val)
57 {
58         return __sync_add_and_fetch (val, 1);
59 }
60 #endif
61
62 /* mingw is missing InterlockedDecrement64 () from winbase.h */
63 #if HAVE_DECL_INTERLOCKEDDECREMENT64==0
64 static inline gint64 InterlockedDecrement64(volatile gint64 *val)
65 {
66         return __sync_sub_and_fetch (val, 1);
67 }
68 #endif
69
70 /* mingw is missing InterlockedAdd () from winbase.h */
71 #if HAVE_DECL_INTERLOCKEDADD==0
72 static inline gint32 InterlockedAdd(volatile gint32 *dest, gint32 add)
73 {
74         return __sync_add_and_fetch (dest, add);
75 }
76 #endif
77
78 /* mingw is missing InterlockedAdd64 () from winbase.h */
79 #if HAVE_DECL_INTERLOCKEDADD64==0
80 static inline gint64 InterlockedAdd64(volatile gint64 *dest, gint64 add)
81 {
82         return __sync_add_and_fetch (dest, add);
83 }
84 #endif
85
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)
89 {
90         return InterlockedExchangeAdd (dest, add) + add;
91 }
92 #endif
93
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)
98 {
99         return InterlockedExchangeAdd64 (dest, add) + add;
100 }
101 #else
102 static inline gint64 InterlockedAdd64(volatile gint64 *dest, gint64 add)
103 {
104         gint64 prev_value;
105
106         do {
107                 prev_value = *dest;
108         } while (prev_value != InterlockedCompareExchange64(dest, prev_value + add, prev_value));
109
110         return prev_value + add;
111 }
112 #endif
113 #endif
114
115 #ifdef HOST_WIN32
116 #define TO_INTERLOCKED_ARGP(ptr) ((volatile LONG*)(ptr))
117 #else
118 #define TO_INTERLOCKED_ARGP(ptr) (ptr)
119 #endif
120
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. */
124
125 static inline gint32 InterlockedRead(volatile gint32 *src)
126 {
127         return InterlockedCompareExchange (TO_INTERLOCKED_ARGP (src), 0, 0);
128 }
129
130 static inline gint64 InterlockedRead64(volatile gint64 *src)
131 {
132         return InterlockedCompareExchange64 (src, 0, 0);
133 }
134
135 static inline gpointer InterlockedReadPointer(volatile gpointer *src)
136 {
137         return InterlockedCompareExchangePointer (src, NULL, NULL);
138 }
139
140 static inline void InterlockedWrite(volatile gint32 *dst, gint32 val)
141 {
142         InterlockedExchange (TO_INTERLOCKED_ARGP (dst), val);
143 }
144
145 static inline void InterlockedWrite64(volatile gint64 *dst, gint64 val)
146 {
147         InterlockedExchange64 (dst, val);
148 }
149
150 static inline void InterlockedWritePointer(volatile gpointer *dst, gpointer val)
151 {
152         InterlockedExchangePointer (dst, val);
153 }
154
155 /* We can't even use CAS for these, so write them out
156  * explicitly according to x86(_64) semantics... */
157
158 static inline gint8 InterlockedRead8(volatile gint8 *src)
159 {
160         return *src;
161 }
162
163 static inline gint16 InterlockedRead16(volatile gint16 *src)
164 {
165         return *src;
166 }
167
168 static inline void InterlockedWrite8(volatile gint8 *dst, gint8 val)
169 {
170         *dst = val;
171         mono_memory_barrier ();
172 }
173
174 static inline void InterlockedWrite16(volatile gint16 *dst, gint16 val)
175 {
176         *dst = val;
177         mono_memory_barrier ();
178 }
179
180 /* Prefer GCC atomic ops if the target supports it (see configure.ac). */
181 #elif defined(USE_GCC_ATOMIC_OPS)
182
183 static inline gint32 InterlockedCompareExchange(volatile gint32 *dest,
184                                                 gint32 exch, gint32 comp)
185 {
186         return __sync_val_compare_and_swap (dest, comp, exch);
187 }
188
189 static inline gpointer InterlockedCompareExchangePointer(volatile gpointer *dest, gpointer exch, gpointer comp)
190 {
191         return __sync_val_compare_and_swap (dest, comp, exch);
192 }
193
194 static inline gint32 InterlockedAdd(volatile gint32 *dest, gint32 add)
195 {
196         return __sync_add_and_fetch (dest, add);
197 }
198
199 static inline gint32 InterlockedIncrement(volatile gint32 *val)
200 {
201         return __sync_add_and_fetch (val, 1);
202 }
203
204 static inline gint32 InterlockedDecrement(volatile gint32 *val)
205 {
206         return __sync_sub_and_fetch (val, 1);
207 }
208
209 static inline gint32 InterlockedExchange(volatile gint32 *val, gint32 new_val)
210 {
211         gint32 old_val;
212         do {
213                 old_val = *val;
214         } while (__sync_val_compare_and_swap (val, old_val, new_val) != old_val);
215         return old_val;
216 }
217
218 static inline gpointer InterlockedExchangePointer(volatile gpointer *val,
219                                                   gpointer new_val)
220 {
221         gpointer old_val;
222         do {
223                 old_val = *val;
224         } while (__sync_val_compare_and_swap (val, old_val, new_val) != old_val);
225         return old_val;
226 }
227
228 static inline gint32 InterlockedExchangeAdd(volatile gint32 *val, gint32 add)
229 {
230         return __sync_fetch_and_add (val, add);
231 }
232
233 static inline gint8 InterlockedRead8(volatile gint8 *src)
234 {
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);
238 }
239
240 static inline gint16 InterlockedRead16(volatile gint16 *src)
241 {
242         return __sync_fetch_and_add (src, 0);
243 }
244
245 static inline gint32 InterlockedRead(volatile gint32 *src)
246 {
247         return __sync_fetch_and_add (src, 0);
248 }
249
250 static inline void InterlockedWrite8(volatile gint8 *dst, gint8 val)
251 {
252         /* Nothing useful from GCC at all, so fall back to CAS. */
253         gint8 old_val;
254         do {
255                 old_val = *dst;
256         } while (__sync_val_compare_and_swap (dst, old_val, val) != old_val);
257 }
258
259 static inline void InterlockedWrite16(volatile gint16 *dst, gint16 val)
260 {
261         gint16 old_val;
262         do {
263                 old_val = *dst;
264         } while (__sync_val_compare_and_swap (dst, old_val, val) != old_val);
265 }
266
267 static inline void InterlockedWrite(volatile gint32 *dst, gint32 val)
268 {
269         /* Nothing useful from GCC at all, so fall back to CAS. */
270         gint32 old_val;
271         do {
272                 old_val = *dst;
273         } while (__sync_val_compare_and_swap (dst, old_val, val) != old_val);
274 }
275
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
278 #endif
279
280 #if !defined (BROKEN_64BIT_ATOMICS_INTRINSIC)
281
282 static inline gint64 InterlockedCompareExchange64(volatile gint64 *dest, gint64 exch, gint64 comp)
283 {
284         return __sync_val_compare_and_swap (dest, comp, exch);
285 }
286
287 static inline gint64 InterlockedAdd64(volatile gint64 *dest, gint64 add)
288 {
289         return __sync_add_and_fetch (dest, add);
290 }
291
292 static inline gint64 InterlockedIncrement64(volatile gint64 *val)
293 {
294         return __sync_add_and_fetch (val, 1);
295 }
296
297 static inline gint64 InterlockedDecrement64(volatile gint64 *val)
298 {
299         return __sync_sub_and_fetch (val, 1);
300 }
301
302 static inline gint64 InterlockedExchangeAdd64(volatile gint64 *val, gint64 add)
303 {
304         return __sync_fetch_and_add (val, add);
305 }
306
307 static inline gint64 InterlockedRead64(volatile gint64 *src)
308 {
309         /* Kind of a hack, but GCC doesn't give us anything better. */
310         return __sync_fetch_and_add (src, 0);
311 }
312
313 #else
314
315 /* Implement 64-bit cmpxchg by hand or emulate it. */
316 extern gint64 InterlockedCompareExchange64(volatile gint64 *dest, gint64 exch, gint64 comp);
317
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.
321  */
322
323 static inline gint64 InterlockedExchangeAdd64(volatile gint64 *dest, gint64 add)
324 {
325         gint64 old_val;
326         do {
327                 old_val = *dest;
328         } while (InterlockedCompareExchange64 (dest, old_val + add, old_val) != old_val);
329         return old_val;
330 }
331
332 static inline gint64 InterlockedIncrement64(volatile gint64 *val)
333 {
334         gint64 get, set;
335         do {
336                 get = *val;
337                 set = get + 1;
338         } while (InterlockedCompareExchange64 (val, set, get) != get);
339         return set;
340 }
341
342 static inline gint64 InterlockedDecrement64(volatile gint64 *val)
343 {
344         gint64 get, set;
345         do {
346                 get = *val;
347                 set = get - 1;
348         } while (InterlockedCompareExchange64 (val, set, get) != get);
349         return set;
350 }
351
352 static inline gint64 InterlockedAdd64(volatile gint64 *dest, gint64 add)
353 {
354         gint64 get, set;
355         do {
356                 get = *dest;
357                 set = get + add;
358         } while (InterlockedCompareExchange64 (dest, set, get) != get);
359         return set;
360 }
361
362 static inline gint64 InterlockedRead64(volatile gint64 *src)
363 {
364         return InterlockedCompareExchange64 (src, 0, 0);
365 }
366
367 #endif
368
369 static inline gpointer InterlockedReadPointer(volatile gpointer *src)
370 {
371         return InterlockedCompareExchangePointer (src, NULL, NULL);
372 }
373
374 static inline void InterlockedWritePointer(volatile gpointer *dst, gpointer val)
375 {
376         InterlockedExchangePointer (dst, val);
377 }
378
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)
382 {
383         gint64 old_val;
384         do {
385                 old_val = *val;
386         } while (InterlockedCompareExchange64 (val, new_val, old_val) != old_val);
387         return old_val;
388 }
389
390 static inline void InterlockedWrite64(volatile gint64 *dst, gint64 val)
391 {
392         /* Nothing useful from GCC at all, so fall back to CAS. */
393         InterlockedExchange64 (dst, val);
394 }
395
396 #elif defined(__ia64__)
397
398 #ifdef __INTEL_COMPILER
399 #include <ia64intrin.h>
400 #endif
401
402 static inline gint32 InterlockedCompareExchange(gint32 volatile *dest,
403                                                 gint32 exch, gint32 comp)
404 {
405         gint32 old;
406         guint64 real_comp;
407
408 #ifdef __INTEL_COMPILER
409         old = _InterlockedCompareExchange (dest, exch, comp);
410 #else
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));
416 #endif
417
418         return(old);
419 }
420
421 static inline gpointer InterlockedCompareExchangePointer(gpointer volatile *dest,
422                                                 gpointer exch, gpointer comp)
423 {
424         gpointer old;
425
426 #ifdef __INTEL_COMPILER
427         old = _InterlockedCompareExchangePointer (dest, exch, comp);
428 #else
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));
432 #endif
433
434         return(old);
435 }
436
437 static inline gint32 InterlockedIncrement(gint32 volatile *val)
438 {
439 #ifdef __INTEL_COMPILER
440         return _InterlockedIncrement (val);
441 #else
442         gint32 old;
443
444         do {
445                 old = *val;
446         } while (InterlockedCompareExchange (val, old + 1, old) != old);
447
448         return old + 1;
449 #endif
450 }
451
452 static inline gint32 InterlockedDecrement(gint32 volatile *val)
453 {
454 #ifdef __INTEL_COMPILER
455         return _InterlockedDecrement (val);
456 #else
457         gint32 old;
458
459         do {
460                 old = *val;
461         } while (InterlockedCompareExchange (val, old - 1, old) != old);
462
463         return old - 1;
464 #endif
465 }
466
467 static inline gint32 InterlockedExchange(gint32 volatile *dest, gint32 new_val)
468 {
469 #ifdef __INTEL_COMPILER
470         return _InterlockedExchange (dest, new_val);
471 #else
472         gint32 res;
473
474         do {
475                 res = *dest;
476         } while (InterlockedCompareExchange (dest, new_val, res) != res);
477
478         return res;
479 #endif
480 }
481
482 static inline gpointer InterlockedExchangePointer(gpointer volatile *dest, gpointer new_val)
483 {
484 #ifdef __INTEL_COMPILER
485         return (gpointer)_InterlockedExchange64 ((gint64*)dest, (gint64)new_val);
486 #else
487         gpointer res;
488
489         do {
490                 res = *dest;
491         } while (InterlockedCompareExchangePointer (dest, new_val, res) != res);
492
493         return res;
494 #endif
495 }
496
497 static inline gint32 InterlockedExchangeAdd(gint32 volatile *val, gint32 add)
498 {
499         gint32 old;
500
501 #ifdef __INTEL_COMPILER
502         old = _InterlockedExchangeAdd (val, add);
503 #else
504         do {
505                 old = *val;
506         } while (InterlockedCompareExchange (val, old + add, old) != old);
507
508         return old;
509 #endif
510 }
511
512 #else
513
514 #define WAPI_NO_ATOMIC_ASM
515
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);
540
541 #endif
542
543 #endif /* _WAPI_ATOMIC_H_ */