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