Home | History | Annotate | Download | only in glib
      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  * Copyright (C) 2007 Nokia Corporation
      7  *
      8  * This library is free software; you can redistribute it and/or
      9  * modify it under the terms of the GNU Lesser General Public
     10  * License as published by the Free Software Foundation; either
     11  * version 2 of the License, or (at your option) any later version.
     12  *
     13  * This library is distributed in the hope that it will be useful,
     14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     16  * Lesser General Public License for more details.
     17  *
     18  * You should have received a copy of the GNU Lesser General Public
     19  * License along with this library; if not, write to the
     20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
     21  * Boston, MA 02111-1307, USA.
     22  */
     23 
     24 #include "config.h"
     25 
     26 #if defined (G_ATOMIC_ARM)
     27 #include <sched.h>
     28 #endif
     29 
     30 #include "glib.h"
     31 #include "gthreadprivate.h"
     32 #include "galias.h"
     33 
     34 #if defined (__GNUC__)
     35 # if defined (G_ATOMIC_I486)
     36 /* Adapted from CVS version 1.10 of glibc's sysdeps/i386/i486/bits/atomic.h
     37  */
     38 gint
     39 g_atomic_int_exchange_and_add (volatile gint G_GNUC_MAY_ALIAS *atomic,
     40 			       gint           val)
     41 {
     42   gint result;
     43 
     44   __asm__ __volatile__ ("lock; xaddl %0,%1"
     45                         : "=r" (result), "=m" (*atomic)
     46 			: "0" (val), "m" (*atomic));
     47   return result;
     48 }
     49 
     50 void
     51 g_atomic_int_add (volatile gint G_GNUC_MAY_ALIAS *atomic,
     52 		  gint           val)
     53 {
     54   __asm__ __volatile__ ("lock; addl %1,%0"
     55 			: "=m" (*atomic)
     56 			: "ir" (val), "m" (*atomic));
     57 }
     58 
     59 gboolean
     60 g_atomic_int_compare_and_exchange (volatile gint G_GNUC_MAY_ALIAS *atomic,
     61 				   gint           oldval,
     62 				   gint           newval)
     63 {
     64   gint result;
     65 
     66   __asm__ __volatile__ ("lock; cmpxchgl %2, %1"
     67 			: "=a" (result), "=m" (*atomic)
     68 			: "r" (newval), "m" (*atomic), "0" (oldval));
     69 
     70   return result == oldval;
     71 }
     72 
     73 /* The same code as above, as on i386 gpointer is 32 bit as well.
     74  * Duplicating the code here seems more natural than casting the
     75  * arguments and calling the former function */
     76 
     77 gboolean
     78 g_atomic_pointer_compare_and_exchange (volatile gpointer G_GNUC_MAY_ALIAS *atomic,
     79 				       gpointer           oldval,
     80 				       gpointer           newval)
     81 {
     82   gpointer result;
     83 
     84   __asm__ __volatile__ ("lock; cmpxchgl %2, %1"
     85 			: "=a" (result), "=m" (*atomic)
     86 			: "r" (newval), "m" (*atomic), "0" (oldval));
     87 
     88   return result == oldval;
     89 }
     90 
     91 # elif defined (G_ATOMIC_SPARCV9)
     92 /* Adapted from CVS version 1.3 of glibc's sysdeps/sparc/sparc64/bits/atomic.h
     93  */
     94 #  define ATOMIC_INT_CMP_XCHG(atomic, oldval, newval)			\
     95   ({ 									\
     96      gint __result;							\
     97      __asm__ __volatile__ ("cas [%4], %2, %0"				\
     98                            : "=r" (__result), "=m" (*(atomic))		\
     99                            : "r" (oldval), "m" (*(atomic)), "r" (atomic),\
    100                            "0" (newval));				\
    101      __result == oldval;						\
    102   })
    103 
    104 #  if GLIB_SIZEOF_VOID_P == 4 /* 32-bit system */
    105 gboolean
    106 g_atomic_pointer_compare_and_exchange (volatile gpointer G_GNUC_MAY_ALIAS *atomic,
    107 				       gpointer           oldval,
    108 				       gpointer           newval)
    109 {
    110   gpointer result;
    111   __asm__ __volatile__ ("cas [%4], %2, %0"
    112 			: "=r" (result), "=m" (*atomic)
    113 			: "r" (oldval), "m" (*atomic), "r" (atomic),
    114 			"0" (newval));
    115   return result == oldval;
    116 }
    117 #  elif GLIB_SIZEOF_VOID_P == 8 /* 64-bit system */
    118 gboolean
    119 g_atomic_pointer_compare_and_exchange (volatile gpointer G_GNUC_MAY_ALIAS *atomic,
    120 				       gpointer           oldval,
    121 				       gpointer           newval)
    122 {
    123   gpointer result;
    124   gpointer *a = atomic;
    125   __asm__ __volatile__ ("casx [%4], %2, %0"
    126 			: "=r" (result), "=m" (*a)
    127 			: "r" (oldval), "m" (*a), "r" (a),
    128 			"0" (newval));
    129   return result == oldval;
    130 }
    131 #  else /* What's that */
    132 #    error "Your system has an unsupported pointer size"
    133 #  endif /* GLIB_SIZEOF_VOID_P */
    134 #  define G_ATOMIC_MEMORY_BARRIER					\
    135   __asm__ __volatile__ ("membar #LoadLoad | #LoadStore"			\
    136                         " | #StoreLoad | #StoreStore" : : : "memory")
    137 
    138 # elif defined (G_ATOMIC_ALPHA)
    139 /* Adapted from CVS version 1.3 of glibc's sysdeps/alpha/bits/atomic.h
    140  */
    141 #  define ATOMIC_INT_CMP_XCHG(atomic, oldval, newval)			\
    142   ({ 									\
    143      gint __result;							\
    144      gint __prev;							\
    145      __asm__ __volatile__ (						\
    146         "       mb\n"							\
    147         "1:     ldl_l   %0,%2\n"					\
    148         "       cmpeq   %0,%3,%1\n"					\
    149         "       beq     %1,2f\n"					\
    150         "       mov     %4,%1\n"					\
    151         "       stl_c   %1,%2\n"					\
    152         "       beq     %1,1b\n"					\
    153         "       mb\n"							\
    154         "2:"								\
    155         : "=&r" (__prev), 						\
    156           "=&r" (__result)						\
    157         : "m" (*(atomic)),						\
    158           "Ir" (oldval),						\
    159           "Ir" (newval)							\
    160         : "memory");							\
    161      __result != 0;							\
    162   })
    163 #  if GLIB_SIZEOF_VOID_P == 4 /* 32-bit system */
    164 gboolean
    165 g_atomic_pointer_compare_and_exchange (volatile gpointer G_GNUC_MAY_ALIAS *atomic,
    166 				       gpointer           oldval,
    167 				       gpointer           newval)
    168 {
    169   gint result;
    170   gpointer prev;
    171   __asm__ __volatile__ (
    172         "       mb\n"
    173         "1:     ldl_l   %0,%2\n"
    174         "       cmpeq   %0,%3,%1\n"
    175         "       beq     %1,2f\n"
    176         "       mov     %4,%1\n"
    177         "       stl_c   %1,%2\n"
    178         "       beq     %1,1b\n"
    179         "       mb\n"
    180         "2:"
    181         : "=&r" (prev),
    182           "=&r" (result)
    183         : "m" (*atomic),
    184           "Ir" (oldval),
    185           "Ir" (newval)
    186         : "memory");
    187   return result != 0;
    188 }
    189 #  elif GLIB_SIZEOF_VOID_P == 8 /* 64-bit system */
    190 gboolean
    191 g_atomic_pointer_compare_and_exchange (volatile gpointer G_GNUC_MAY_ALIAS *atomic,
    192 				       gpointer           oldval,
    193 				       gpointer           newval)
    194 {
    195   gint result;
    196   gpointer prev;
    197   __asm__ __volatile__ (
    198         "       mb\n"
    199         "1:     ldq_l   %0,%2\n"
    200         "       cmpeq   %0,%3,%1\n"
    201         "       beq     %1,2f\n"
    202         "       mov     %4,%1\n"
    203         "       stq_c   %1,%2\n"
    204         "       beq     %1,1b\n"
    205         "       mb\n"
    206         "2:"
    207         : "=&r" (prev),
    208           "=&r" (result)
    209         : "m" (*atomic),
    210           "Ir" (oldval),
    211           "Ir" (newval)
    212         : "memory");
    213   return result != 0;
    214 }
    215 #  else /* What's that */
    216 #   error "Your system has an unsupported pointer size"
    217 #  endif /* GLIB_SIZEOF_VOID_P */
    218 #  define G_ATOMIC_MEMORY_BARRIER  __asm__ ("mb" : : : "memory")
    219 # elif defined (G_ATOMIC_X86_64)
    220 /* Adapted from CVS version 1.9 of glibc's sysdeps/x86_64/bits/atomic.h
    221  */
    222 gint
    223 g_atomic_int_exchange_and_add (volatile gint G_GNUC_MAY_ALIAS *atomic,
    224 			       gint           val)
    225 {
    226   gint result;
    227 
    228   __asm__ __volatile__ ("lock; xaddl %0,%1"
    229                         : "=r" (result), "=m" (*atomic)
    230 			: "0" (val), "m" (*atomic));
    231   return result;
    232 }
    233 
    234 void
    235 g_atomic_int_add (volatile gint G_GNUC_MAY_ALIAS *atomic,
    236 		  gint           val)
    237 {
    238   __asm__ __volatile__ ("lock; addl %1,%0"
    239 			: "=m" (*atomic)
    240 			: "ir" (val), "m" (*atomic));
    241 }
    242 
    243 gboolean
    244 g_atomic_int_compare_and_exchange (volatile gint G_GNUC_MAY_ALIAS *atomic,
    245 				   gint           oldval,
    246 				   gint           newval)
    247 {
    248   gint result;
    249 
    250   __asm__ __volatile__ ("lock; cmpxchgl %2, %1"
    251 			: "=a" (result), "=m" (*atomic)
    252 			: "r" (newval), "m" (*atomic), "0" (oldval));
    253 
    254   return result == oldval;
    255 }
    256 
    257 gboolean
    258 g_atomic_pointer_compare_and_exchange (volatile gpointer G_GNUC_MAY_ALIAS *atomic,
    259 				       gpointer           oldval,
    260 				       gpointer           newval)
    261 {
    262   gpointer result;
    263 
    264   __asm__ __volatile__ ("lock; cmpxchgq %q2, %1"
    265 			: "=a" (result), "=m" (*atomic)
    266 			: "r" (newval), "m" (*atomic), "0" (oldval));
    267 
    268   return result == oldval;
    269 }
    270 
    271 # elif defined (G_ATOMIC_POWERPC)
    272 /* Adapted from CVS version 1.16 of glibc's sysdeps/powerpc/bits/atomic.h
    273  * and CVS version 1.4 of glibc's sysdeps/powerpc/powerpc32/bits/atomic.h
    274  * and CVS version 1.7 of glibc's sysdeps/powerpc/powerpc64/bits/atomic.h
    275  */
    276 #   ifdef __OPTIMIZE__
    277 /* Non-optimizing compile bails on the following two asm statements
    278  * for reasons unknown to the author */
    279 gint
    280 g_atomic_int_exchange_and_add (volatile gint G_GNUC_MAY_ALIAS *atomic,
    281 			       gint           val)
    282 {
    283   gint result, temp;
    284 #if ASM_NUMERIC_LABELS
    285   __asm__ __volatile__ ("1:       lwarx   %0,0,%3\n"
    286 			"         add     %1,%0,%4\n"
    287 			"         stwcx.  %1,0,%3\n"
    288 			"         bne-    1b"
    289 			: "=&b" (result), "=&r" (temp), "=m" (*atomic)
    290 			: "b" (atomic), "r" (val), "m" (*atomic)
    291 			: "cr0", "memory");
    292 #else
    293   __asm__ __volatile__ (".Lieaa%=:       lwarx   %0,0,%3\n"
    294 			"         add     %1,%0,%4\n"
    295 			"         stwcx.  %1,0,%3\n"
    296 			"         bne-    .Lieaa%="
    297 			: "=&b" (result), "=&r" (temp), "=m" (*atomic)
    298 			: "b" (atomic), "r" (val), "m" (*atomic)
    299 			: "cr0", "memory");
    300 #endif
    301   return result;
    302 }
    303 
    304 /* The same as above, to save a function call repeated here */
    305 void
    306 g_atomic_int_add (volatile gint G_GNUC_MAY_ALIAS *atomic,
    307 		  gint           val)
    308 {
    309   gint result, temp;
    310 #if ASM_NUMERIC_LABELS
    311   __asm__ __volatile__ ("1:       lwarx   %0,0,%3\n"
    312 			"         add     %1,%0,%4\n"
    313 			"         stwcx.  %1,0,%3\n"
    314 			"         bne-    1b"
    315 			: "=&b" (result), "=&r" (temp), "=m" (*atomic)
    316 			: "b" (atomic), "r" (val), "m" (*atomic)
    317 			: "cr0", "memory");
    318 #else
    319   __asm__ __volatile__ (".Lia%=:       lwarx   %0,0,%3\n"
    320 			"         add     %1,%0,%4\n"
    321 			"         stwcx.  %1,0,%3\n"
    322 			"         bne-    .Lia%="
    323 			: "=&b" (result), "=&r" (temp), "=m" (*atomic)
    324 			: "b" (atomic), "r" (val), "m" (*atomic)
    325 			: "cr0", "memory");
    326 #endif
    327 }
    328 #   else /* !__OPTIMIZE__ */
    329 gint
    330 g_atomic_int_exchange_and_add (volatile gint G_GNUC_MAY_ALIAS *atomic,
    331 			       gint           val)
    332 {
    333   gint result;
    334   do
    335     result = *atomic;
    336   while (!g_atomic_int_compare_and_exchange (atomic, result, result + val));
    337 
    338   return result;
    339 }
    340 
    341 void
    342 g_atomic_int_add (volatile gint G_GNUC_MAY_ALIAS *atomic,
    343 		  gint           val)
    344 {
    345   gint result;
    346   do
    347     result = *atomic;
    348   while (!g_atomic_int_compare_and_exchange (atomic, result, result + val));
    349 }
    350 #   endif /* !__OPTIMIZE__ */
    351 
    352 #   if GLIB_SIZEOF_VOID_P == 4 /* 32-bit system */
    353 gboolean
    354 g_atomic_int_compare_and_exchange (volatile gint G_GNUC_MAY_ALIAS *atomic,
    355 				   gint           oldval,
    356 				   gint           newval)
    357 {
    358   gint result;
    359 #if ASM_NUMERIC_LABELS
    360   __asm__ __volatile__ ("sync\n"
    361 			"1: lwarx   %0,0,%1\n"
    362 			"   subf.   %0,%2,%0\n"
    363 			"   bne     2f\n"
    364 			"   stwcx.  %3,0,%1\n"
    365 			"   bne-    1b\n"
    366 			"2: isync"
    367 			: "=&r" (result)
    368 			: "b" (atomic), "r" (oldval), "r" (newval)
    369 			: "cr0", "memory");
    370 #else
    371   __asm__ __volatile__ ("sync\n"
    372 			".L1icae%=: lwarx   %0,0,%1\n"
    373 			"   subf.   %0,%2,%0\n"
    374 			"   bne     .L2icae%=\n"
    375 			"   stwcx.  %3,0,%1\n"
    376 			"   bne-    .L1icae%=\n"
    377 			".L2icae%=: isync"
    378 			: "=&r" (result)
    379 			: "b" (atomic), "r" (oldval), "r" (newval)
    380 			: "cr0", "memory");
    381 #endif
    382   return result == 0;
    383 }
    384 
    385 gboolean
    386 g_atomic_pointer_compare_and_exchange (volatile gpointer G_GNUC_MAY_ALIAS *atomic,
    387 				       gpointer           oldval,
    388 				       gpointer           newval)
    389 {
    390   gpointer result;
    391 #if ASM_NUMERIC_LABELS
    392   __asm__ __volatile__ ("sync\n"
    393 			"1: lwarx   %0,0,%1\n"
    394 			"   subf.   %0,%2,%0\n"
    395 			"   bne     2f\n"
    396 			"   stwcx.  %3,0,%1\n"
    397 			"   bne-    1b\n"
    398 			"2: isync"
    399 			: "=&r" (result)
    400 			: "b" (atomic), "r" (oldval), "r" (newval)
    401 			: "cr0", "memory");
    402 #else
    403   __asm__ __volatile__ ("sync\n"
    404 			".L1pcae%=: lwarx   %0,0,%1\n"
    405 			"   subf.   %0,%2,%0\n"
    406 			"   bne     .L2pcae%=\n"
    407 			"   stwcx.  %3,0,%1\n"
    408 			"   bne-    .L1pcae%=\n"
    409 			".L2pcae%=: isync"
    410 			: "=&r" (result)
    411 			: "b" (atomic), "r" (oldval), "r" (newval)
    412 			: "cr0", "memory");
    413 #endif
    414   return result == 0;
    415 }
    416 #   elif GLIB_SIZEOF_VOID_P == 8 /* 64-bit system */
    417 gboolean
    418 g_atomic_int_compare_and_exchange (volatile gint G_GNUC_MAY_ALIAS *atomic,
    419 				   gint           oldval,
    420 				   gint           newval)
    421 {
    422   gpointer result;
    423 #if ASM_NUMERIC_LABELS
    424   __asm__ __volatile__ ("sync\n"
    425 			"1: lwarx   %0,0,%1\n"
    426 			"   extsw   %0,%0\n"
    427 			"   subf.   %0,%2,%0\n"
    428 			"   bne     2f\n"
    429 			"   stwcx.  %3,0,%1\n"
    430 			"   bne-    1b\n"
    431 			"2: isync"
    432 			: "=&r" (result)
    433 			: "b" (atomic), "r" (oldval), "r" (newval)
    434 			: "cr0", "memory");
    435 #else
    436   __asm__ __volatile__ ("sync\n"
    437 			".L1icae%=: lwarx   %0,0,%1\n"
    438 			"   extsw   %0,%0\n"
    439 			"   subf.   %0,%2,%0\n"
    440 			"   bne     .L2icae%=\n"
    441 			"   stwcx.  %3,0,%1\n"
    442 			"   bne-    .L1icae%=\n"
    443 			".L2icae%=: isync"
    444 			: "=&r" (result)
    445 			: "b" (atomic), "r" (oldval), "r" (newval)
    446 			: "cr0", "memory");
    447 #endif
    448   return result == 0;
    449 }
    450 
    451 gboolean
    452 g_atomic_pointer_compare_and_exchange (volatile gpointer G_GNUC_MAY_ALIAS *atomic,
    453 				       gpointer           oldval,
    454 				       gpointer           newval)
    455 {
    456   gpointer result;
    457 #if ASM_NUMERIC_LABELS
    458   __asm__ __volatile__ ("sync\n"
    459 			"1: ldarx   %0,0,%1\n"
    460 			"   subf.   %0,%2,%0\n"
    461 			"   bne     2f\n"
    462 			"   stdcx.  %3,0,%1\n"
    463 			"   bne-    1b\n"
    464 			"2: isync"
    465 			: "=&r" (result)
    466 			: "b" (atomic), "r" (oldval), "r" (newval)
    467 			: "cr0", "memory");
    468 #else
    469   __asm__ __volatile__ ("sync\n"
    470 			".L1pcae%=: ldarx   %0,0,%1\n"
    471 			"   subf.   %0,%2,%0\n"
    472 			"   bne     .L2pcae%=\n"
    473 			"   stdcx.  %3,0,%1\n"
    474 			"   bne-    .L1pcae%=\n"
    475 			".L2pcae%=: isync"
    476 			: "=&r" (result)
    477 			: "b" (atomic), "r" (oldval), "r" (newval)
    478 			: "cr0", "memory");
    479 #endif
    480   return result == 0;
    481 }
    482 #  else /* What's that */
    483 #   error "Your system has an unsupported pointer size"
    484 #  endif /* GLIB_SIZEOF_VOID_P */
    485 
    486 #  define G_ATOMIC_MEMORY_BARRIER __asm__ ("sync" : : : "memory")
    487 
    488 # elif defined (G_ATOMIC_IA64)
    489 /* Adapted from CVS version 1.8 of glibc's sysdeps/ia64/bits/atomic.h
    490  */
    491 gint
    492 g_atomic_int_exchange_and_add (volatile gint G_GNUC_MAY_ALIAS *atomic,
    493 			       gint           val)
    494 {
    495   return __sync_fetch_and_add (atomic, val);
    496 }
    497 
    498 void
    499 g_atomic_int_add (volatile gint G_GNUC_MAY_ALIAS *atomic,
    500 		  gint val)
    501 {
    502   __sync_fetch_and_add (atomic, val);
    503 }
    504 
    505 gboolean
    506 g_atomic_int_compare_and_exchange (volatile gint G_GNUC_MAY_ALIAS *atomic,
    507 				   gint           oldval,
    508 				   gint           newval)
    509 {
    510   return __sync_bool_compare_and_swap (atomic, oldval, newval);
    511 }
    512 
    513 gboolean
    514 g_atomic_pointer_compare_and_exchange (volatile gpointer G_GNUC_MAY_ALIAS *atomic,
    515 				       gpointer           oldval,
    516 				       gpointer           newval)
    517 {
    518   return __sync_bool_compare_and_swap ((long *)atomic,
    519 				       (long)oldval, (long)newval);
    520 }
    521 
    522 #  define G_ATOMIC_MEMORY_BARRIER __sync_synchronize ()
    523 # elif defined (G_ATOMIC_S390)
    524 /* Adapted from glibc's sysdeps/s390/bits/atomic.h
    525  */
    526 #  define ATOMIC_INT_CMP_XCHG(atomic, oldval, newval)			\
    527   ({ 									\
    528      gint __result = oldval;					\
    529      __asm__ __volatile__ ("cs %0, %2, %1"				\
    530                            : "+d" (__result), "=Q" (*(atomic))		\
    531                            : "d" (newval), "m" (*(atomic)) : "cc" );	\
    532      __result == oldval;						\
    533   })
    534 
    535 #  if GLIB_SIZEOF_VOID_P == 4 /* 32-bit system */
    536 gboolean
    537 g_atomic_pointer_compare_and_exchange (volatile gpointer G_GNUC_MAY_ALIAS *atomic,
    538 				       gpointer           oldval,
    539 				       gpointer           newval)
    540 {
    541   gpointer result = oldval;
    542   __asm__ __volatile__ ("cs %0, %2, %1"
    543 			: "+d" (result), "=Q" (*(atomic))
    544 			: "d" (newval), "m" (*(atomic)) : "cc" );
    545   return result == oldval;
    546 }
    547 #  elif GLIB_SIZEOF_VOID_P == 8 /* 64-bit system */
    548 gboolean
    549 g_atomic_pointer_compare_and_exchange (volatile gpointer G_GNUC_MAY_ALIAS *atomic,
    550 				       gpointer           oldval,
    551 				       gpointer           newval)
    552 {
    553   gpointer result = oldval;
    554   gpointer *a = atomic;
    555   __asm__ __volatile__ ("csg %0, %2, %1"
    556 			: "+d" (result), "=Q" (*a)
    557 			: "d" ((long)(newval)), "m" (*a) : "cc" );
    558   return result == oldval;
    559 }
    560 #  else /* What's that */
    561 #    error "Your system has an unsupported pointer size"
    562 #  endif /* GLIB_SIZEOF_VOID_P */
    563 # elif defined (G_ATOMIC_ARM)
    564 static volatile int atomic_spin = 0;
    565 
    566 static int atomic_spin_trylock (void)
    567 {
    568   int result;
    569 
    570   asm volatile (
    571     "swp %0, %1, [%2]\n"
    572     : "=&r,&r" (result)
    573     : "r,0" (1), "r,r" (&atomic_spin)
    574     : "memory");
    575   if (result == 0)
    576     return 0;
    577   else
    578     return -1;
    579 }
    580 
    581 static void atomic_spin_lock (void)
    582 {
    583   while (atomic_spin_trylock())
    584     sched_yield();
    585 }
    586 
    587 static void atomic_spin_unlock (void)
    588 {
    589   atomic_spin = 0;
    590 }
    591 
    592 gint
    593 g_atomic_int_exchange_and_add (volatile gint G_GNUC_MAY_ALIAS *atomic,
    594 			       gint           val)
    595 {
    596   gint result;
    597 
    598   atomic_spin_lock();
    599   result = *atomic;
    600   *atomic += val;
    601   atomic_spin_unlock();
    602 
    603   return result;
    604 }
    605 
    606 void
    607 g_atomic_int_add (volatile gint G_GNUC_MAY_ALIAS *atomic,
    608 		  gint           val)
    609 {
    610   atomic_spin_lock();
    611   *atomic += val;
    612   atomic_spin_unlock();
    613 }
    614 
    615 gboolean
    616 g_atomic_int_compare_and_exchange (volatile gint G_GNUC_MAY_ALIAS *atomic,
    617 				   gint           oldval,
    618 				   gint           newval)
    619 {
    620   gboolean result;
    621 
    622   atomic_spin_lock();
    623   if (*atomic == oldval)
    624     {
    625       result = TRUE;
    626       *atomic = newval;
    627     }
    628   else
    629     result = FALSE;
    630   atomic_spin_unlock();
    631 
    632   return result;
    633 }
    634 
    635 gboolean
    636 g_atomic_pointer_compare_and_exchange (volatile gpointer G_GNUC_MAY_ALIAS *atomic,
    637 				       gpointer           oldval,
    638 				       gpointer           newval)
    639 {
    640   gboolean result;
    641 
    642   atomic_spin_lock();
    643   if (*atomic == oldval)
    644     {
    645       result = TRUE;
    646       *atomic = newval;
    647     }
    648   else
    649     result = FALSE;
    650   atomic_spin_unlock();
    651 
    652   return result;
    653 }
    654 # elif defined (G_ATOMIC_CRIS) || defined (G_ATOMIC_CRISV32)
    655 #  ifdef G_ATOMIC_CRIS
    656 #   define CRIS_ATOMIC_INT_CMP_XCHG(atomic, oldval, newval)		\
    657   ({									\
    658      gboolean __result;							\
    659      __asm__ __volatile__ ("\n"						\
    660                            "0:\tclearf\n\t"				\
    661                            "cmp.d [%[Atomic]], %[OldVal]\n\t"		\
    662                            "bne 1f\n\t"					\
    663                            "ax\n\t"					\
    664                            "move.d %[NewVal], [%[Atomic]]\n\t"		\
    665                            "bwf 0b\n"					\
    666                            "1:\tseq %[Result]"				\
    667                            : [Result] "=&r" (__result),			\
    668                                       "=m" (*(atomic))			\
    669                            : [Atomic] "r" (atomic),			\
    670                              [OldVal] "r" (oldval),			\
    671                              [NewVal] "r" (newval),			\
    672                                       "g" (*(gpointer*) (atomic))	\
    673                            : "memory");					\
    674      __result;								\
    675   })
    676 #  else
    677 #   define CRIS_ATOMIC_INT_CMP_XCHG(atomic, oldval, newval)		\
    678   ({									\
    679      gboolean __result;							\
    680      __asm__ __volatile__ ("\n"						\
    681                            "0:\tclearf p\n\t"				\
    682                            "cmp.d [%[Atomic]], %[OldVal]\n\t"		\
    683                            "bne 1f\n\t"					\
    684                            "ax\n\t"					\
    685                            "move.d %[NewVal], [%[Atomic]]\n\t"		\
    686                            "bcs 0b\n"					\
    687                            "1:\tseq %[Result]"				\
    688                            : [Result] "=&r" (__result),			\
    689                                       "=m" (*(atomic))			\
    690                            : [Atomic] "r" (atomic),			\
    691                              [OldVal] "r" (oldval),			\
    692                              [NewVal] "r" (newval),			\
    693                                       "g" (*(gpointer*) (atomic))	\
    694                            : "memory");					\
    695      __result;								\
    696   })
    697 #  endif
    698 
    699 #define CRIS_CACHELINE_SIZE 32
    700 #define CRIS_ATOMIC_BREAKS_CACHELINE(atomic) \
    701   (((gulong)(atomic) & (CRIS_CACHELINE_SIZE - 1)) > (CRIS_CACHELINE_SIZE - sizeof (atomic)))
    702 
    703 gint     __g_atomic_int_exchange_and_add         (volatile gint   G_GNUC_MAY_ALIAS *atomic,
    704 						  gint             val);
    705 void     __g_atomic_int_add                      (volatile gint   G_GNUC_MAY_ALIAS *atomic,
    706 						  gint             val);
    707 gboolean __g_atomic_int_compare_and_exchange     (volatile gint   G_GNUC_MAY_ALIAS *atomic,
    708 						  gint             oldval,
    709 						  gint             newval);
    710 gboolean __g_atomic_pointer_compare_and_exchange (volatile gpointer G_GNUC_MAY_ALIAS *atomic,
    711 						  gpointer         oldval,
    712 						  gpointer         newval);
    713 
    714 gboolean
    715 g_atomic_pointer_compare_and_exchange (volatile gpointer G_GNUC_MAY_ALIAS *atomic,
    716 				       gpointer           oldval,
    717 				       gpointer           newval)
    718 {
    719   if (G_UNLIKELY (CRIS_ATOMIC_BREAKS_CACHELINE (atomic)))
    720     return __g_atomic_pointer_compare_and_exchange (atomic, oldval, newval);
    721 
    722   return CRIS_ATOMIC_INT_CMP_XCHG (atomic, oldval, newval);
    723 }
    724 
    725 gboolean
    726 g_atomic_int_compare_and_exchange (volatile gint G_GNUC_MAY_ALIAS *atomic,
    727 				   gint           oldval,
    728 				   gint           newval)
    729 {
    730   if (G_UNLIKELY (CRIS_ATOMIC_BREAKS_CACHELINE (atomic)))
    731     return __g_atomic_int_compare_and_exchange (atomic, oldval, newval);
    732 
    733   return CRIS_ATOMIC_INT_CMP_XCHG (atomic, oldval, newval);
    734 }
    735 
    736 gint
    737 g_atomic_int_exchange_and_add (volatile gint G_GNUC_MAY_ALIAS *atomic,
    738 			       gint           val)
    739 {
    740   gint result;
    741 
    742   if (G_UNLIKELY (CRIS_ATOMIC_BREAKS_CACHELINE (atomic)))
    743     return __g_atomic_int_exchange_and_add (atomic, val);
    744 
    745   do
    746     result = *atomic;
    747   while (!CRIS_ATOMIC_INT_CMP_XCHG (atomic, result, result + val));
    748 
    749   return result;
    750 }
    751 
    752 void
    753 g_atomic_int_add (volatile gint G_GNUC_MAY_ALIAS *atomic,
    754 		  gint           val)
    755 {
    756   gint result;
    757 
    758   if (G_UNLIKELY (CRIS_ATOMIC_BREAKS_CACHELINE (atomic)))
    759     return __g_atomic_int_add (atomic, val);
    760 
    761   do
    762     result = *atomic;
    763   while (!CRIS_ATOMIC_INT_CMP_XCHG (atomic, result, result + val));
    764 }
    765 
    766 /* We need the atomic mutex for atomic operations where the atomic variable
    767  * breaks the 32 byte cache line since the CRIS architecture does not support
    768  * atomic operations on such variables. Fortunately this should be rare.
    769  */
    770 #  define DEFINE_WITH_MUTEXES
    771 #  define g_atomic_int_exchange_and_add __g_atomic_int_exchange_and_add
    772 #  define g_atomic_int_add __g_atomic_int_add
    773 #  define g_atomic_int_compare_and_exchange __g_atomic_int_compare_and_exchange
    774 #  define g_atomic_pointer_compare_and_exchange __g_atomic_pointer_compare_and_exchange
    775 
    776 # else /* !G_ATOMIC_* */
    777 #  define DEFINE_WITH_MUTEXES
    778 # endif /* G_ATOMIC_* */
    779 #else /* !__GNUC__ */
    780 # ifdef G_PLATFORM_WIN32
    781 #  define DEFINE_WITH_WIN32_INTERLOCKED
    782 # else
    783 #  define DEFINE_WITH_MUTEXES
    784 # endif
    785 #endif /* __GNUC__ */
    786 
    787 #ifdef DEFINE_WITH_WIN32_INTERLOCKED
    788 # include <windows.h>
    789 /* Following indicates that InterlockedCompareExchangePointer is
    790  * declared in winbase.h (included by windows.h) and needs to be
    791  * commented out if not true. It is defined iff WINVER > 0x0400,
    792  * which is usually correct but can be wrong if WINVER is set before
    793  * windows.h is included.
    794  */
    795 # if WINVER > 0x0400
    796 #  define HAVE_INTERLOCKED_COMPARE_EXCHANGE_POINTER
    797 # endif
    798 
    799 gint32
    800 g_atomic_int_exchange_and_add (volatile gint32 G_GNUC_MAY_ALIAS *atomic,
    801 			       gint32           val)
    802 {
    803   return InterlockedExchangeAdd (atomic, val);
    804 }
    805 
    806 void
    807 g_atomic_int_add (volatile gint32 G_GNUC_MAY_ALIAS *atomic,
    808 		  gint32           val)
    809 {
    810   InterlockedExchangeAdd (atomic, val);
    811 }
    812 
    813 gboolean
    814 g_atomic_int_compare_and_exchange (volatile gint32 G_GNUC_MAY_ALIAS *atomic,
    815 				   gint32           oldval,
    816 				   gint32           newval)
    817 {
    818 #ifndef HAVE_INTERLOCKED_COMPARE_EXCHANGE_POINTER
    819   return (guint32) InterlockedCompareExchange ((PVOID*)atomic,
    820                                                (PVOID)newval,
    821                                                (PVOID)oldval) == oldval;
    822 #else
    823   return InterlockedCompareExchange (atomic,
    824                                      newval,
    825                                      oldval) == oldval;
    826 #endif
    827 }
    828 
    829 gboolean
    830 g_atomic_pointer_compare_and_exchange (volatile gpointer G_GNUC_MAY_ALIAS *atomic,
    831 				       gpointer           oldval,
    832 				       gpointer           newval)
    833 {
    834 # ifdef HAVE_INTERLOCKED_COMPARE_EXCHANGE_POINTER
    835   return InterlockedCompareExchangePointer (atomic, newval, oldval) == oldval;
    836 # else
    837 #  if GLIB_SIZEOF_VOID_P != 4 /* no 32-bit system */
    838 #   error "InterlockedCompareExchangePointer needed"
    839 #  else
    840    return InterlockedCompareExchange (atomic, newval, oldval) == oldval;
    841 #  endif
    842 # endif
    843 }
    844 #endif /* DEFINE_WITH_WIN32_INTERLOCKED */
    845 
    846 #ifdef DEFINE_WITH_MUTEXES
    847 /* We have to use the slow, but safe locking method */
    848 static GMutex *g_atomic_mutex;
    849 
    850 gint
    851 g_atomic_int_exchange_and_add (volatile gint G_GNUC_MAY_ALIAS *atomic,
    852 			       gint           val)
    853 {
    854   gint result;
    855 
    856   g_mutex_lock (g_atomic_mutex);
    857   result = *atomic;
    858   *atomic += val;
    859   g_mutex_unlock (g_atomic_mutex);
    860 
    861   return result;
    862 }
    863 
    864 
    865 void
    866 g_atomic_int_add (volatile gint G_GNUC_MAY_ALIAS *atomic,
    867 		  gint           val)
    868 {
    869   g_mutex_lock (g_atomic_mutex);
    870   *atomic += val;
    871   g_mutex_unlock (g_atomic_mutex);
    872 }
    873 
    874 gboolean
    875 g_atomic_int_compare_and_exchange (volatile gint G_GNUC_MAY_ALIAS *atomic,
    876 				   gint           oldval,
    877 				   gint           newval)
    878 {
    879   gboolean result;
    880 
    881   g_mutex_lock (g_atomic_mutex);
    882   if (*atomic == oldval)
    883     {
    884       result = TRUE;
    885       *atomic = newval;
    886     }
    887   else
    888     result = FALSE;
    889   g_mutex_unlock (g_atomic_mutex);
    890 
    891   return result;
    892 }
    893 
    894 gboolean
    895 g_atomic_pointer_compare_and_exchange (volatile gpointer G_GNUC_MAY_ALIAS *atomic,
    896 				       gpointer           oldval,
    897 				       gpointer           newval)
    898 {
    899   gboolean result;
    900 
    901   g_mutex_lock (g_atomic_mutex);
    902   if (*atomic == oldval)
    903     {
    904       result = TRUE;
    905       *atomic = newval;
    906     }
    907   else
    908     result = FALSE;
    909   g_mutex_unlock (g_atomic_mutex);
    910 
    911   return result;
    912 }
    913 
    914 #ifdef G_ATOMIC_OP_MEMORY_BARRIER_NEEDED
    915 gint
    916 (g_atomic_int_get) (volatile gint G_GNUC_MAY_ALIAS *atomic)
    917 {
    918   gint result;
    919 
    920   g_mutex_lock (g_atomic_mutex);
    921   result = *atomic;
    922   g_mutex_unlock (g_atomic_mutex);
    923 
    924   return result;
    925 }
    926 
    927 void
    928 (g_atomic_int_set) (volatile gint G_GNUC_MAY_ALIAS *atomic,
    929                   gint           newval)
    930 {
    931   g_mutex_lock (g_atomic_mutex);
    932   *atomic = newval;
    933   g_mutex_unlock (g_atomic_mutex);
    934 }
    935 
    936 gpointer
    937 (g_atomic_pointer_get) (volatile gpointer G_GNUC_MAY_ALIAS *atomic)
    938 {
    939   gpointer result;
    940 
    941   g_mutex_lock (g_atomic_mutex);
    942   result = *atomic;
    943   g_mutex_unlock (g_atomic_mutex);
    944 
    945   return result;
    946 }
    947 
    948 void
    949 (g_atomic_pointer_set) (volatile gpointer G_GNUC_MAY_ALIAS *atomic,
    950                       gpointer           newval)
    951 {
    952   g_mutex_lock (g_atomic_mutex);
    953   *atomic = newval;
    954   g_mutex_unlock (g_atomic_mutex);
    955 }
    956 #endif /* G_ATOMIC_OP_MEMORY_BARRIER_NEEDED */
    957 #elif defined (G_ATOMIC_OP_MEMORY_BARRIER_NEEDED)
    958 gint
    959 (g_atomic_int_get) (volatile gint G_GNUC_MAY_ALIAS *atomic)
    960 {
    961   G_ATOMIC_MEMORY_BARRIER;
    962   return *atomic;
    963 }
    964 
    965 void
    966 (g_atomic_int_set) (volatile gint G_GNUC_MAY_ALIAS *atomic,
    967                   gint           newval)
    968 {
    969   *atomic = newval;
    970   G_ATOMIC_MEMORY_BARRIER;
    971 }
    972 
    973 gpointer
    974 (g_atomic_pointer_get) (volatile gpointer G_GNUC_MAY_ALIAS *atomic)
    975 {
    976   G_ATOMIC_MEMORY_BARRIER;
    977   return *atomic;
    978 }
    979 
    980 void
    981 (g_atomic_pointer_set) (volatile gpointer G_GNUC_MAY_ALIAS *atomic,
    982                       gpointer           newval)
    983 {
    984   *atomic = newval;
    985   G_ATOMIC_MEMORY_BARRIER;
    986 }
    987 #endif /* DEFINE_WITH_MUTEXES || G_ATOMIC_OP_MEMORY_BARRIER_NEEDED */
    988 
    989 #ifdef ATOMIC_INT_CMP_XCHG
    990 gboolean
    991 g_atomic_int_compare_and_exchange (volatile gint G_GNUC_MAY_ALIAS *atomic,
    992 				   gint           oldval,
    993 				   gint           newval)
    994 {
    995   return ATOMIC_INT_CMP_XCHG (atomic, oldval, newval);
    996 }
    997 
    998 gint
    999 g_atomic_int_exchange_and_add (volatile gint G_GNUC_MAY_ALIAS *atomic,
   1000 			       gint           val)
   1001 {
   1002   gint result;
   1003   do
   1004     result = *atomic;
   1005   while (!ATOMIC_INT_CMP_XCHG (atomic, result, result + val));
   1006 
   1007   return result;
   1008 }
   1009 
   1010 void
   1011 g_atomic_int_add (volatile gint G_GNUC_MAY_ALIAS *atomic,
   1012 		  gint           val)
   1013 {
   1014   gint result;
   1015   do
   1016     result = *atomic;
   1017   while (!ATOMIC_INT_CMP_XCHG (atomic, result, result + val));
   1018 }
   1019 #endif /* ATOMIC_INT_CMP_XCHG */
   1020 
   1021 void
   1022 _g_atomic_thread_init (void)
   1023 {
   1024 #ifdef DEFINE_WITH_MUTEXES
   1025   g_atomic_mutex = g_mutex_new ();
   1026 #endif /* DEFINE_WITH_MUTEXES */
   1027 }
   1028 
   1029 #ifndef G_ATOMIC_OP_MEMORY_BARRIER_NEEDED
   1030 gint
   1031 (g_atomic_int_get) (volatile gint G_GNUC_MAY_ALIAS *atomic)
   1032 {
   1033   return g_atomic_int_get (atomic);
   1034 }
   1035 
   1036 void
   1037 (g_atomic_int_set) (volatile gint G_GNUC_MAY_ALIAS *atomic,
   1038 		    gint           newval)
   1039 {
   1040   g_atomic_int_set (atomic, newval);
   1041 }
   1042 
   1043 gpointer
   1044 (g_atomic_pointer_get) (volatile gpointer G_GNUC_MAY_ALIAS *atomic)
   1045 {
   1046   return g_atomic_pointer_get (atomic);
   1047 }
   1048 
   1049 void
   1050 (g_atomic_pointer_set) (volatile gpointer G_GNUC_MAY_ALIAS *atomic,
   1051 			gpointer           newval)
   1052 {
   1053   g_atomic_pointer_set (atomic, newval);
   1054 }
   1055 #endif /* G_ATOMIC_OP_MEMORY_BARRIER_NEEDED */
   1056 
   1057 #define __G_ATOMIC_C__
   1058 #include "galiasdef.c"
   1059