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