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