Merge pull request #5714 from alexischr/update_bockbuild
[mono.git] / mono / utils / atomic.c
1 /**
2  * \file
3  * Workarounds for atomic operations for platforms that dont have
4  * really atomic asm functions in atomic.h
5  *
6  * Author:
7  *      Dick Porter (dick@ximian.com)
8  *
9  * (C) 2002 Ximian, Inc.
10  */
11
12 #include <config.h>
13 #include <glib.h>
14
15 #include <mono/utils/atomic.h>
16 #include <mono/utils/mono-compiler.h>
17
18 #if defined (WAPI_NO_ATOMIC_ASM) || defined (BROKEN_64BIT_ATOMICS_INTRINSIC)
19
20 #include <pthread.h>
21
22 static pthread_mutex_t spin G_GNUC_UNUSED = PTHREAD_MUTEX_INITIALIZER;
23
24 #define NEED_64BIT_CMPXCHG_FALLBACK
25
26 #endif
27
28 #ifdef WAPI_NO_ATOMIC_ASM
29
30 gint32 InterlockedCompareExchange(volatile gint32 *dest, gint32 exch,
31                                   gint32 comp)
32 {
33         gint32 old;
34         int ret;
35         
36         pthread_cleanup_push ((void(*)(void *))pthread_mutex_unlock,
37                               (void *)&spin);
38         ret = pthread_mutex_lock(&spin);
39         g_assert (ret == 0);
40         
41         old= *dest;
42         if(old==comp) {
43                 *dest=exch;
44         }
45         
46         ret = pthread_mutex_unlock(&spin);
47         g_assert (ret == 0);
48         
49         pthread_cleanup_pop (0);
50
51         return(old);
52 }
53
54 gpointer InterlockedCompareExchangePointer(volatile gpointer *dest,
55                                            gpointer exch, gpointer comp)
56 {
57         gpointer old;
58         int ret;
59         
60         pthread_cleanup_push ((void(*)(void *))pthread_mutex_unlock,
61                               (void *)&spin);
62         ret = pthread_mutex_lock(&spin);
63         g_assert (ret == 0);
64         
65         old= *dest;
66         if(old==comp) {
67                 *dest=exch;
68         }
69         
70         ret = pthread_mutex_unlock(&spin);
71         g_assert (ret == 0);
72         
73         pthread_cleanup_pop (0);
74
75         return(old);
76 }
77
78 gint32 InterlockedAdd(volatile gint32 *dest, gint32 add)
79 {
80         gint32 ret;
81         int thr_ret;
82
83         pthread_cleanup_push ((void(*)(void *))pthread_mutex_unlock,
84                               (void *)&spin);
85         thr_ret = pthread_mutex_lock(&spin);
86         g_assert (thr_ret == 0);
87
88         *dest += add;
89         ret= *dest;
90
91         thr_ret = pthread_mutex_unlock(&spin);
92         g_assert (thr_ret == 0);
93
94         pthread_cleanup_pop (0);
95
96         return(ret);
97 }
98
99 gint64 InterlockedAdd64(volatile gint64 *dest, gint64 add)
100 {
101         gint64 ret;
102         int thr_ret;
103
104         pthread_cleanup_push ((void(*)(void *))pthread_mutex_unlock,
105                               (void *)&spin);
106         thr_ret = pthread_mutex_lock(&spin);
107         g_assert (thr_ret == 0);
108
109         *dest += add;
110         ret= *dest;
111
112         thr_ret = pthread_mutex_unlock(&spin);
113         g_assert (thr_ret == 0);
114
115         pthread_cleanup_pop (0);
116
117         return(ret);
118 }
119
120 gint32 InterlockedIncrement(volatile gint32 *dest)
121 {
122         gint32 ret;
123         int thr_ret;
124         
125         pthread_cleanup_push ((void(*)(void *))pthread_mutex_unlock,
126                               (void *)&spin);
127         thr_ret = pthread_mutex_lock(&spin);
128         g_assert (thr_ret == 0);
129
130         (*dest)++;
131         ret= *dest;
132         
133         thr_ret = pthread_mutex_unlock(&spin);
134         g_assert (thr_ret == 0);
135         
136         pthread_cleanup_pop (0);
137         
138         return(ret);
139 }
140
141 gint64 InterlockedIncrement64(volatile gint64 *dest)
142 {
143         gint64 ret;
144         int thr_ret;
145
146         pthread_cleanup_push ((void(*)(void *))pthread_mutex_unlock,
147                               (void *)&spin);
148         thr_ret = pthread_mutex_lock(&spin);
149         g_assert (thr_ret == 0);
150
151         (*dest)++;
152         ret= *dest;
153
154         thr_ret = pthread_mutex_unlock(&spin);
155         g_assert (thr_ret == 0);
156
157         pthread_cleanup_pop (0);
158
159         return(ret);
160 }
161
162 gint32 InterlockedDecrement(volatile gint32 *dest)
163 {
164         gint32 ret;
165         int thr_ret;
166         
167         pthread_cleanup_push ((void(*)(void *))pthread_mutex_unlock,
168                               (void *)&spin);
169         thr_ret = pthread_mutex_lock(&spin);
170         g_assert (thr_ret == 0);
171         
172         (*dest)--;
173         ret= *dest;
174         
175         thr_ret = pthread_mutex_unlock(&spin);
176         g_assert (thr_ret == 0);
177         
178         pthread_cleanup_pop (0);
179         
180         return(ret);
181 }
182
183 gint64 InterlockedDecrement64(volatile gint64 *dest)
184 {
185         gint64 ret;
186         int thr_ret;
187
188         pthread_cleanup_push ((void(*)(void *))pthread_mutex_unlock,
189                               (void *)&spin);
190         thr_ret = pthread_mutex_lock(&spin);
191         g_assert (thr_ret == 0);
192
193         (*dest)--;
194         ret= *dest;
195
196         thr_ret = pthread_mutex_unlock(&spin);
197         g_assert (thr_ret == 0);
198
199         pthread_cleanup_pop (0);
200
201         return(ret);
202 }
203
204 gint32 InterlockedExchange(volatile gint32 *dest, gint32 exch)
205 {
206         gint32 ret;
207         int thr_ret;
208         
209         pthread_cleanup_push ((void(*)(void *))pthread_mutex_unlock,
210                               (void *)&spin);
211         thr_ret = pthread_mutex_lock(&spin);
212         g_assert (thr_ret == 0);
213
214         ret=*dest;
215         *dest=exch;
216         
217         thr_ret = pthread_mutex_unlock(&spin);
218         g_assert (thr_ret == 0);
219         
220         pthread_cleanup_pop (0);
221         
222         return(ret);
223 }
224
225 gint64 InterlockedExchange64(volatile gint64 *dest, gint64 exch)
226 {
227         gint64 ret;
228         int thr_ret;
229
230         pthread_cleanup_push ((void(*)(void *))pthread_mutex_unlock,
231                               (void *)&spin);
232         thr_ret = pthread_mutex_lock(&spin);
233         g_assert (thr_ret == 0);
234
235         ret=*dest;
236         *dest=exch;
237
238         thr_ret = pthread_mutex_unlock(&spin);
239         g_assert (thr_ret == 0);
240
241         pthread_cleanup_pop (0);
242
243         return(ret);
244 }
245
246 gpointer InterlockedExchangePointer(volatile gpointer *dest, gpointer exch)
247 {
248         gpointer ret;
249         int thr_ret;
250         
251         pthread_cleanup_push ((void(*)(void *))pthread_mutex_unlock,
252                               (void *)&spin);
253         thr_ret = pthread_mutex_lock(&spin);
254         g_assert (thr_ret == 0);
255         
256         ret=*dest;
257         *dest=exch;
258         
259         thr_ret = pthread_mutex_unlock(&spin);
260         g_assert (thr_ret == 0);
261         
262         pthread_cleanup_pop (0);
263         
264         return(ret);
265 }
266
267 gint32 InterlockedExchangeAdd(volatile gint32 *dest, gint32 add)
268 {
269         gint32 ret;
270         int thr_ret;
271         
272         pthread_cleanup_push ((void(*)(void *))pthread_mutex_unlock,
273                               (void *)&spin);
274         thr_ret = pthread_mutex_lock(&spin);
275         g_assert (thr_ret == 0);
276
277         ret= *dest;
278         *dest+=add;
279         
280         thr_ret = pthread_mutex_unlock(&spin);
281         g_assert (thr_ret == 0);
282
283         pthread_cleanup_pop (0);
284
285         return(ret);
286 }
287
288 gint64 InterlockedExchangeAdd64(volatile gint64 *dest, gint64 add)
289 {
290         gint64 ret;
291         int thr_ret;
292         
293         pthread_cleanup_push ((void(*)(void *))pthread_mutex_unlock,
294                               (void *)&spin);
295         thr_ret = pthread_mutex_lock(&spin);
296         g_assert (thr_ret == 0);
297
298         ret= *dest;
299         *dest+=add;
300         
301         thr_ret = pthread_mutex_unlock(&spin);
302         g_assert (thr_ret == 0);
303
304         pthread_cleanup_pop (0);
305
306         return(ret);
307 }
308
309 gint8 InterlockedRead8(volatile gint8 *src)
310 {
311         gint8 ret;
312         int thr_ret;
313         
314         pthread_cleanup_push ((void(*)(void *))pthread_mutex_unlock,
315                               (void *)&spin);
316         thr_ret = pthread_mutex_lock(&spin);
317         g_assert (thr_ret == 0);
318
319         ret= *src;
320         
321         thr_ret = pthread_mutex_unlock(&spin);
322         g_assert (thr_ret == 0);
323
324         pthread_cleanup_pop (0);
325
326         return(ret);
327 }
328
329 gint16 InterlockedRead16(volatile gint16 *src)
330 {
331         gint16 ret;
332         int thr_ret;
333         
334         pthread_cleanup_push ((void(*)(void *))pthread_mutex_unlock,
335                               (void *)&spin);
336         thr_ret = pthread_mutex_lock(&spin);
337         g_assert (thr_ret == 0);
338
339         ret= *src;
340         
341         thr_ret = pthread_mutex_unlock(&spin);
342         g_assert (thr_ret == 0);
343
344         pthread_cleanup_pop (0);
345
346         return(ret);
347 }
348
349 gint32 InterlockedRead(volatile gint32 *src)
350 {
351         gint32 ret;
352         int thr_ret;
353         
354         pthread_cleanup_push ((void(*)(void *))pthread_mutex_unlock,
355                               (void *)&spin);
356         thr_ret = pthread_mutex_lock(&spin);
357         g_assert (thr_ret == 0);
358
359         ret= *src;
360         
361         thr_ret = pthread_mutex_unlock(&spin);
362         g_assert (thr_ret == 0);
363
364         pthread_cleanup_pop (0);
365
366         return(ret);
367 }
368
369 gint64 InterlockedRead64(volatile gint64 *src)
370 {
371         gint64 ret;
372         int thr_ret;
373         
374         pthread_cleanup_push ((void(*)(void *))pthread_mutex_unlock,
375                               (void *)&spin);
376         thr_ret = pthread_mutex_lock(&spin);
377         g_assert (thr_ret == 0);
378
379         ret= *src;
380         
381         thr_ret = pthread_mutex_unlock(&spin);
382         g_assert (thr_ret == 0);
383
384         pthread_cleanup_pop (0);
385
386         return(ret);
387 }
388
389 gpointer InterlockedReadPointer(volatile gpointer *src)
390 {
391         gpointer ret;
392         int thr_ret;
393         
394         pthread_cleanup_push ((void(*)(void *))pthread_mutex_unlock,
395                               (void *)&spin);
396         thr_ret = pthread_mutex_lock(&spin);
397         g_assert (thr_ret == 0);
398
399         ret= *src;
400         
401         thr_ret = pthread_mutex_unlock(&spin);
402         g_assert (thr_ret == 0);
403
404         pthread_cleanup_pop (0);
405
406         return(ret);
407 }
408
409 void InterlockedWrite8(volatile gint8 *dst, gint8 val)
410 {
411         int thr_ret;
412         
413         pthread_cleanup_push ((void(*)(void *))pthread_mutex_unlock,
414                               (void *)&spin);
415         thr_ret = pthread_mutex_lock(&spin);
416         g_assert (thr_ret == 0);
417
418         *dst=val;
419         
420         thr_ret = pthread_mutex_unlock(&spin);
421         g_assert (thr_ret == 0);
422         
423         pthread_cleanup_pop (0);
424 }
425
426 void InterlockedWrite16(volatile gint16 *dst, gint16 val)
427 {
428         int thr_ret;
429         
430         pthread_cleanup_push ((void(*)(void *))pthread_mutex_unlock,
431                               (void *)&spin);
432         thr_ret = pthread_mutex_lock(&spin);
433         g_assert (thr_ret == 0);
434
435         *dst=val;
436         
437         thr_ret = pthread_mutex_unlock(&spin);
438         g_assert (thr_ret == 0);
439         
440         pthread_cleanup_pop (0);
441 }
442
443 void InterlockedWrite(volatile gint32 *dst, gint32 val)
444 {
445         int thr_ret;
446         
447         pthread_cleanup_push ((void(*)(void *))pthread_mutex_unlock,
448                               (void *)&spin);
449         thr_ret = pthread_mutex_lock(&spin);
450         g_assert (thr_ret == 0);
451
452         *dst=val;
453         
454         thr_ret = pthread_mutex_unlock(&spin);
455         g_assert (thr_ret == 0);
456         
457         pthread_cleanup_pop (0);
458 }
459
460 void InterlockedWrite64(volatile gint64 *dst, gint64 val)
461 {
462         int thr_ret;
463         
464         pthread_cleanup_push ((void(*)(void *))pthread_mutex_unlock,
465                               (void *)&spin);
466         thr_ret = pthread_mutex_lock(&spin);
467         g_assert (thr_ret == 0);
468
469         *dst=val;
470         
471         thr_ret = pthread_mutex_unlock(&spin);
472         g_assert (thr_ret == 0);
473         
474         pthread_cleanup_pop (0);
475 }
476
477 void InterlockedWritePointer(volatile gpointer *dst, gpointer val)
478 {
479         int thr_ret;
480         
481         pthread_cleanup_push ((void(*)(void *))pthread_mutex_unlock,
482                               (void *)&spin);
483         thr_ret = pthread_mutex_lock(&spin);
484         g_assert (thr_ret == 0);
485
486         *dst=val;
487         
488         thr_ret = pthread_mutex_unlock(&spin);
489         g_assert (thr_ret == 0);
490         
491         pthread_cleanup_pop (0);
492 }
493
494 #endif
495
496 #if defined (NEED_64BIT_CMPXCHG_FALLBACK)
497
498 #if defined (TARGET_OSX)
499
500 /* The compiler breaks if this code is in the header... */
501
502 gint64
503 InterlockedCompareExchange64(volatile gint64 *dest, gint64 exch, gint64 comp)
504 {
505         return __sync_val_compare_and_swap (dest, comp, exch);
506 }
507
508 #elif defined (__arm__) && defined (HAVE_ARMV7) && (defined(TARGET_IOS) || defined(TARGET_WATCHOS) || defined(TARGET_ANDROID))
509
510 #if defined (TARGET_IOS) || defined (TARGET_WATCHOS)
511
512 #ifndef __clang__
513 #error "Not supported."
514 #endif
515
516 gint64
517 InterlockedCompareExchange64(volatile gint64 *dest, gint64 exch, gint64 comp)
518 {
519         return  __sync_val_compare_and_swap (dest, comp, exch);
520 }
521
522 #elif defined (TARGET_ANDROID)
523
524 /* Some Android systems can't find the 64-bit CAS intrinsic at runtime,
525  * so we have to roll our own...
526  */
527
528 gint64 InterlockedCompareExchange64(volatile gint64 *dest, gint64 exch, gint64 comp) __attribute__ ((__naked__));
529
530 gint64
531 InterlockedCompareExchange64(volatile gint64 *dest, gint64 exch, gint64 comp)
532 {
533         __asm__ (
534                 "push           {r4, r5, r6, r7}\n"
535                 "ldrd           r4, [sp, #16]\n"
536                 "dmb            sy\n"
537         "1:\n"
538                 "ldrexd         r6, [r0]\n"
539                 "cmp            r7, r5\n"
540                 "cmpeq          r6, r4\n"
541                 "bne            2f\n"
542                 "strexd         r1, r2, [r0]\n"
543                 "cmp            r1, #0\n"
544                 "bne            1b\n"
545         "2:\n"
546                 "dmb            sy\n"
547                 "mov            r0, r6\n"
548                 "mov            r1, r7\n"
549                 "pop            {r4, r5, r6, r7}\n"
550                 "bx                     lr\n"
551         );
552 }
553
554 #else
555
556 #error "Need a 64-bit CAS fallback!"
557
558 #endif
559
560 #else
561
562 gint64
563 InterlockedCompareExchange64(volatile gint64 *dest, gint64 exch, gint64 comp)
564 {
565         gint64 old;
566         int ret;
567         
568         pthread_cleanup_push ((void(*)(void *))pthread_mutex_unlock,
569                               (void *)&spin);
570         ret = pthread_mutex_lock(&spin);
571         g_assert (ret == 0);
572         
573         old= *dest;
574         if(old==comp) {
575                 *dest=exch;
576         }
577         
578         ret = pthread_mutex_unlock(&spin);
579         g_assert (ret == 0);
580         
581         pthread_cleanup_pop (0);
582
583         return(old);
584 }
585
586 #endif
587 #endif
588
589 #if !defined (WAPI_NO_ATOMIC_ASM) && !defined (BROKEN_64BIT_ATOMICS_INTRINSIC) && !defined (NEED_64BIT_CMPXCHG_FALLBACK)
590 MONO_EMPTY_SOURCE_FILE (atomic);
591 #endif