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