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