added win32 api based implementation for G_PLATFORM_WIN32, !__GNUC__
[platform/upstream/glib.git] / glib / gatomic.c
1 /* GLIB - Library of useful routines for C programming
2  * Copyright (C) 1995-1997  Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * g_atomic_*: atomic operations.
5  * Copyright (C) 2003 Sebastian Wilhelmi
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22  
23 #include "config.h"
24
25 #include "glib.h"
26
27 #if defined (__GNUC__)
28 # if defined (G_ATOMIC_I486)
29 /* Adapted from CVS version 1.10 of glibc's sysdeps/i386/i486/bits/atomic.h 
30  */
31 gint
32 g_atomic_int_exchange_and_add (gint *atomic, 
33                                gint val)
34 {
35   gint result;
36
37   __asm__ __volatile__ ("lock; xaddl %0,%1"
38                         : "=r" (result), "=m" (*atomic) 
39                         : "0" (val), "m" (*atomic));
40   return result;
41 }
42  
43 void
44 g_atomic_int_add (gint *atomic, 
45                   gint val)
46 {
47   __asm__ __volatile__ ("lock; addl %1,%0"
48                         : "=m" (*atomic) 
49                         : "ir" (val), "m" (*atomic));
50 }
51
52 gboolean
53 g_atomic_int_compare_and_exchange (gint *atomic, 
54                                    gint oldval, 
55                                    gint newval)
56 {
57   gint result;
58  
59   __asm __volatile ("lock; cmpxchgl %2, %1"
60                     : "=a" (result), "=m" (*atomic)
61                     : "r" (newval), "m" (*atomic), "0" (oldval)); 
62
63   return result == oldval;
64 }
65
66 /* The same code as above, as on i386 gpointer is 32 bit as well.
67  * Duplicating the code here seems more natural than casting the
68  * arguments and calling the former function */
69
70 gboolean
71 g_atomic_pointer_compare_and_exchange (gpointer *atomic, 
72                                        gpointer  oldval, 
73                                        gpointer  newval)
74 {
75   gpointer result;
76  
77   __asm __volatile ("lock; cmpxchgl %2, %1"
78                     : "=a" (result), "=m" (*atomic)
79                     : "r" (newval), "m" (*atomic), "0" (oldval)); 
80
81   return result == oldval;
82 }
83
84 # elif defined (G_ATOMIC_SPARCV9)
85 /* Adapted from CVS version 1.3 of glibc's sysdeps/sparc/sparc64/bits/atomic.h
86  */
87 #  define ATOMIC_INT_CMP_XCHG(atomic, oldval, newval)                   \
88   ({                                                                    \
89      gint __result;                                                     \
90      __asm __volatile ("cas [%4], %2, %0"                               \
91                        : "=r" (__result), "=m" (*(atomic))              \
92                        : "r" (oldval), "m" (*(atomic)), "r" (atomic),   \
93                          "0" (newval));                                 \
94      __result == oldval;                                                \
95   })
96
97 #  if GLIB_SIZEOF_VOID_P == 4 /* 32-bit system */
98 gboolean
99 g_atomic_pointer_compare_and_exchange (gpointer *atomic, 
100                                        gpointer  oldval, 
101                                        gpointer  newval)
102 {
103   gpointer result;
104   __asm __volatile ("cas [%4], %2, %0"
105                     : "=r" (result), "=m" (*atomic)
106                     : "r" (oldval), "m" (*atomic), "r" (atomic),
107                       "0" (newval));
108   return result == oldval;
109 }
110 #  elif GLIB_SIZEOF_VOID_P == 8 /* 64-bit system */
111 gboolean
112 g_atomic_pointer_compare_and_exchange (gpointer *atomic, 
113                                        gpointer  oldval, 
114                                        gpointer  newval)
115 {
116   gpointer result;
117   gpointer *a = atomic;
118   __asm __volatile ("casx [%4], %2, %0"
119                     : "=r" (result), "=m" (*a)
120                     : "r" (oldval), "m" (*a), "r" (a),
121                       "0" (newval));
122   return result != 0;
123 }
124 #  else /* What's that */
125 #    error "Your system has an unsupported pointer size"
126 #  endif /* GLIB_SIZEOF_VOID_P */
127 #  define G_ATOMIC_MEMORY_BARRIER                                       \
128   __asm __volatile ("membar #LoadLoad | #LoadStore"                     \
129                     " | #StoreLoad | #StoreStore" : : : "memory")
130
131 # elif defined (G_ATOMIC_ALPHA)
132 /* Adapted from CVS version 1.3 of glibc's sysdeps/alpha/bits/atomic.h
133  */
134 #  define ATOMIC_INT_CMP_XCHG(atomic, oldval, newval)                   \
135   ({                                                                    \
136      gint __result;                                                     \
137      gint __prev;                                                       \
138      __asm__ __volatile__ (                                             \
139         "       mb\n"                                                   \
140         "1:     ldl_l   %0,%2\n"                                        \
141         "       cmpeq   %0,%3,%1\n"                                     \
142         "       beq     %1,2f\n"                                        \
143         "       mov     %4,%1\n"                                        \
144         "       stl_c   %1,%2\n"                                        \
145         "       beq     %1,1b\n"                                        \
146         "       mb\n"                                                   \
147         "2:"                                                            \
148         : "=&r" (__prev),                                               \
149           "=&r" (__result)                                              \
150         : "m" (*(atomic)),                                              \
151           "Ir" (oldval),                                                \
152           "Ir" (newval)                                                 \
153         : "memory");                                                    \
154      __result != 0;                                                     \
155   })
156 #  if GLIB_SIZEOF_VOID_P == 4 /* 32-bit system */
157 gboolean
158 g_atomic_pointer_compare_and_exchange (gpointer *atomic, 
159                                        gpointer  oldval, 
160                                        gpointer  newval)
161 {
162   gint result;
163   gpointer prev;
164   __asm__ __volatile__ (
165         "       mb\n"
166         "1:     ldl_l   %0,%2\n"
167         "       cmpeq   %0,%3,%1\n"
168         "       beq     %1,2f\n"
169         "       mov     %4,%1\n"
170         "       stl_c   %1,%2\n"
171         "       beq     %1,1b\n"
172         "       mb\n"
173         "2:"
174         : "=&r" (prev), 
175           "=&r" (result)
176         : "m" (*atomic),
177           "Ir" (oldval),
178           "Ir" (newval)
179         : "memory");
180   return result != 0;
181 }
182 #  elif GLIB_SIZEOF_VOID_P == 8 /* 64-bit system */
183 gboolean
184 g_atomic_pointer_compare_and_exchange (gpointer *atomic, 
185                                        gpointer  oldval, 
186                                        gpointer  newval)
187 {
188   gint result;
189   gpointer prev;
190   __asm__ __volatile__ (
191         "       mb\n"
192         "1:     ldq_l   %0,%2\n"
193         "       cmpeq   %0,%3,%1\n"
194         "       beq     %1,2f\n"
195         "       mov     %4,%1\n"
196         "       stq_c   %1,%2\n"
197         "       beq     %1,1b\n"
198         "       mb\n"
199         "2:"
200         : "=&r" (prev), 
201           "=&r" (result)
202         : "m" (*atomic),
203           "Ir" (oldval),
204           "Ir" (newval)
205         : "memory");
206   return result != 0;
207 }
208 #  else /* What's that */
209 #   error "Your system has an unsupported pointer size"
210 #  endif /* GLIB_SIZEOF_VOID_P */
211 #  define G_ATOMIC_MEMORY_BARRIER  __asm ("mb" : : : "memory")
212 # elif defined (G_ATOMIC_X86_64)
213 /* Adapted from CVS version 1.9 of glibc's sysdeps/x86_64/bits/atomic.h 
214  */
215 gint
216 g_atomic_int_exchange_and_add (gint *atomic, 
217                                gint val)
218 {
219   gint result;
220
221   __asm__ __volatile__ ("lock; xaddl %0,%1"
222                         : "=r" (result), "=m" (*atomic) 
223                         : "0" (val), "m" (*atomic));
224   return result;
225 }
226  
227 void
228 g_atomic_int_add (gint *atomic, 
229                   gint val)
230 {
231   __asm__ __volatile__ ("lock; addl %1,%0"
232                         : "=m" (*atomic) 
233                         : "ir" (val), "m" (*atomic));
234 }
235
236 gboolean
237 g_atomic_int_compare_and_exchange (gint *atomic, 
238                                    gint oldval, 
239                                    gint newval)
240 {
241   gint result;
242  
243   __asm __volatile ("lock; cmpxchgl %2, %1"
244                     : "=a" (result), "=m" (*atomic)
245                     : "r" (newval), "m" (*atomic), "0" (oldval)); 
246
247   return result == oldval;
248 }
249
250 gboolean
251 g_atomic_pointer_compare_and_exchange (gpointer *atomic, 
252                                        gpointer  oldval, 
253                                        gpointer  newval)
254 {
255   gpointer result;
256  
257   __asm __volatile ("lock; cmpxchgq %q2, %1"
258                     : "=a" (result), "=m" (*atomic)
259                     : "r" (newval), "m" (*atomic), "0" (oldval)); 
260
261   return result == oldval;
262 }
263
264 # elif defined (G_ATOMIC_POWERPC)
265 /* Adapted from CVS version 1.12 of glibc's sysdeps/powerpc/bits/atomic.h 
266  * and CVS version 1.3 of glibc's sysdeps/powerpc/powerpc32/bits/atomic.h 
267  * and CVS version 1.2 of glibc's sysdeps/powerpc/powerpc64/bits/atomic.h 
268  */
269 gint
270 g_atomic_int_exchange_and_add (gint *atomic, 
271                                gint val)
272 {
273   gint result, temp;
274   __asm __volatile ("1:       lwarx   %0,0,%3\n"
275                     "         add     %1,%0,%4\n"
276                     "         stwcx.  %1,0,%3\n"
277                     "         bne-    1b"
278                     : "=&b" (result), "=&r" (temp), "=m" (*atomic)
279                     : "b" (atomic), "r" (val), "2" (*atomic)
280                     : "cr0", "memory");
281   return result;
282 }
283  
284 /* The same as above, to save a function call repeated here */
285 void
286 g_atomic_int_add (gint *atomic, 
287                   gint val)
288 {
289   gint result, temp;  
290   __asm __volatile ("1:       lwarx   %0,0,%3\n"
291                     "         add     %1,%0,%4\n"
292                     "         stwcx.  %1,0,%3\n"
293                     "         bne-    1b"
294                     : "=&b" (result), "=&r" (temp), "=m" (*atomic)
295                     : "b" (atomic), "r" (val), "2" (*atomic)
296                     : "cr0", "memory");
297 }
298
299 #   if GLIB_SIZEOF_VOID_P == 4 /* 32-bit system */
300 gboolean
301 g_atomic_int_compare_and_exchange (gint *atomic, 
302                                    gint oldval, 
303                                    gint newval)
304 {
305   gint result;
306   __asm __volatile ("sync\n"
307                     "1: lwarx   %0,0,%1\n"
308                     "   subf.   %0,%2,%0\n"
309                     "   bne     2f\n"
310                     "   stwcx.  %3,0,%1\n"
311                     "   bne-    1b\n"
312                     "2: isync"
313                     : "=&r" (result)
314                     : "b" (atomic), "r" (oldval), "r" (newval)
315                     : "cr0", "memory"); 
316   return result == 0;
317 }
318
319 gboolean
320 g_atomic_pointer_compare_and_exchange (gpointer *atomic, 
321                                        gpointer  oldval, 
322                                        gpointer  newval)
323 {
324   gpointer result;
325   __asm __volatile ("sync\n"
326                     "1: lwarx   %0,0,%1\n"
327                     "   subf.   %0,%2,%0\n"
328                     "   bne     2f\n"
329                     "   stwcx.  %3,0,%1\n"
330                     "   bne-    1b\n"
331                     "2: isync"
332                     : "=&r" (result)
333                     : "b" (atomic), "r" (oldval), "r" (newval)
334                     : "cr0", "memory"); 
335   return result == 0;
336 }
337 #   elif GLIB_SIZEOF_VOID_P == 8 /* 64-bit system */
338 gboolean
339 g_atomic_int_compare_and_exchange (gint *atomic, 
340                                    gint oldval, 
341                                    gint newval)
342 {
343   __asm __volatile ("sync\n"
344                     "1: lwarx   %0,0,%1\n"
345                     "   extsw   %0,%0\n"
346                     "   subf.   %0,%2,%0\n"
347                     "   bne     2f\n"
348                     "   stwcx.  %3,0,%1\n"
349                     "   bne-    1b\n"
350                     "2: isync"
351                     : "=&r" (result)
352                     : "b" (atomic), "r" (oldval), "r" (newval)
353                     : "cr0", "memory"); 
354   return result == 0;
355 }
356
357 gboolean
358 g_atomic_pointer_compare_and_exchange (gpointer *atomic, 
359                                        gpointer  oldval, 
360                                        gpointer  newval)
361 {
362   gpointer result;
363   __asm __volatile ("sync\n"
364                     "1: ldarx   %0,0,%1\n"
365                     "   subf.   %0,%2,%0\n"
366                     "   bne     2f\n"
367                     "   stdcx.  %3,0,%1\n"
368                     "   bne-    1b\n"
369                     "2: isync"
370                     : "=&r" (result)
371                     : "b" (atomic), "r" (oldval), "r" (newval)
372                     : "cr0", "memory"); 
373   return result == 0;
374 }
375 #  else /* What's that */
376 #   error "Your system has an unsupported pointer size"
377 #  endif /* GLIB_SIZEOF_VOID_P */
378
379 #  define G_ATOMIC_MEMORY_BARRIER __asm ("sync" : : : "memory")
380
381 # elif defined (G_ATOMIC_IA64)
382 /* Adapted from CVS version 1.8 of glibc's sysdeps/ia64/bits/atomic.h
383  */
384 gint
385 g_atomic_int_exchange_and_add (gint *atomic, 
386                                gint val)
387 {
388   return __sync_fetch_and_add_si (atomic, val);
389 }
390  
391 void
392 g_atomic_int_add (gint *atomic, 
393                   gint val)
394 {
395   __sync_fetch_and_add_si (atomic, val);
396 }
397
398 gboolean
399 g_atomic_int_compare_and_exchange (gint *atomic, 
400                                    gint oldval, 
401                                    gint newval)
402 {
403   return __sync_bool_compare_and_swap_si (atomic, oldval, newval);
404 }
405
406 gboolean
407 g_atomic_pointer_compare_and_exchange (gpointer *atomic, 
408                                        gpointer  oldval, 
409                                        gpointer  newval)
410 {
411   return __sync_bool_compare_and_swap_di ((long *)atomic, 
412                                           (long)oldval, (long)newval);
413 }
414
415 #  define G_ATOMIC_MEMORY_BARRIER __sync_synchronize ()
416 # else /* !G_ATOMIC */
417 #  define DEFINE_WITH_MUTEXES
418 # endif /* G_ATOMIC */
419 #else /* !__GNUC__ */
420 # ifdef G_PLATFORM_WIN32
421 #  define DEFINE_WITH_WIN32_INTERLOCKED
422 # else
423 #  define DEFINE_WITH_MUTEXES
424 # endif
425 #endif /* __GNUC__ */
426
427 #ifdef DEFINE_WITH_WIN32_INTERLOCKED
428 # include <windows.h>
429 gint32   
430 g_atomic_int_exchange_and_add (gint32   *atomic, 
431                                gint32    val)
432 {
433   return InterlockedExchangeAdd (atomic, val);
434 }
435
436 void     
437 g_atomic_int_add (gint32   *atomic, 
438                   gint32    val)
439 {
440   InterlockedExchangeAdd (atomic, val);
441 }
442
443 gboolean 
444 g_atomic_int_compare_and_exchange (gint32   *atomic, 
445                                    gint32    oldval, 
446                                    gint32    newval)
447 {
448   return (guint32)InterlockedCompareExchange ((PVOID*)atomic, 
449                                               (PVOID)newval, 
450                                               (PVOID)oldval) == oldval;
451 }
452
453 gboolean 
454 g_atomic_pointer_compare_and_exchange (gpointer *atomic, 
455                                        gpointer  oldval, 
456                                        gpointer  newval)
457 {
458 # if GLIB_SIZEOF_VOID_P != 4 /* no 32-bit system */
459 #  error "InterlockedCompareExchangePointer needed"
460 # else
461    return InterlockedCompareExchange (atomic, newval, oldval) == oldval;
462 # endif
463 }
464 #endif /* DEFINE_WITH_WIN32_INTERLOCKED */
465
466 #ifdef DEFINE_WITH_MUTEXES
467 /* We have to use the slow, but safe locking method */
468 G_LOCK_DEFINE_STATIC (g_atomic_lock);
469 gint
470 g_atomic_int_exchange_and_add (gint *atomic, 
471                                gint  val)
472 {
473   gint result;
474     
475   G_LOCK (g_atomic_lock);
476   result = *atomic;
477   *atomic += val;
478   G_UNLOCK (g_atomic_lock);
479
480   return result;
481 }
482
483
484 void
485 g_atomic_int_add (gint *atomic,
486                   gint  val)
487 {
488   G_LOCK (g_atomic_lock);
489   *atomic += val;
490   G_UNLOCK (g_atomic_lock);
491 }
492
493 gboolean
494 g_atomic_int_compare_and_exchange (gint *atomic, 
495                                    gint  oldval, 
496                                    gint  newval)
497 {
498   gboolean result;
499     
500   G_LOCK (g_atomic_lock);
501   if (*atomic == oldval)
502     {
503       result = TRUE;
504       *atomic = newval;
505     }
506   else
507     result = FALSE;
508   G_UNLOCK (g_atomic_lock);
509
510   return result;
511 }
512
513 gboolean
514 g_atomic_pointer_compare_and_exchange (gpointer *atomic, 
515                                        gpointer  oldval, 
516                                        gpointer  newval)
517 {
518   gboolean result;
519     
520   G_LOCK (g_atomic_lock);
521   if (*atomic == oldval)
522     {
523       result = TRUE;
524       *atomic = newval;
525     }
526   else
527     result = FALSE;
528   G_UNLOCK (g_atomic_lock);
529
530   return result;
531 }
532
533 gint
534 g_atomic_int_get (gint *atomic)
535 {
536   gint result;
537
538   G_LOCK (g_atomic_lock);
539   result = *atomic;
540   G_UNLOCK (g_atomic_lock);
541
542   return result;
543 }
544
545 gpointer
546 g_atomic_pointer_get (gpointer *atomic)
547 {
548   gpointer result;
549
550   G_LOCK (g_atomic_lock);
551   result = *atomic;
552   G_UNLOCK (g_atomic_lock);
553
554   return result;
555 }   
556 #elif defined (G_ATOMIC_OP_MEMORY_BARRIER_NEEDED)
557 gint
558 g_atomic_int_get (gint *atomic)
559 {
560   gint result = *atomic;
561
562   G_ATOMIC_MEMORY_BARRIER;
563
564   return result;
565 }
566
567 gpointer
568 g_atomic_pointer_get (gpointer *atomic)
569 {
570   gpointer result = *atomic;
571
572   G_ATOMIC_MEMORY_BARRIER;
573
574   return result;
575 }   
576 #endif /* DEFINE_WITH_MUTEXES || G_ATOMIC_OP_MEMORY_BARRIER_NEEDED */
577
578 #ifdef ATOMIC_INT_CMP_XCHG
579 gboolean
580 g_atomic_int_compare_and_exchange (gint *atomic, 
581                                    gint oldval, 
582                                    gint newval)
583 {
584   return ATOMIC_INT_CMP_XCHG (atomic, oldval, newval);
585 }
586
587 gint
588 g_atomic_int_exchange_and_add (gint *atomic, 
589                                gint val)
590 {
591   gint result;
592   do
593     result = *atomic;
594   while (!ATOMIC_INT_CMP_XCHG (atomic, result, result + val));
595
596   return result;
597 }
598  
599 void
600 g_atomic_int_add (gint *atomic, 
601                   gint val)
602 {
603   gint result;
604   do
605     result = *atomic;
606   while (!ATOMIC_INT_CMP_XCHG (atomic, result, result + val));
607 }
608 #endif /* ATOMIC_INT_CMP_XCHG */