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