• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
g_atomic_int_exchange_and_add(volatile gint G_GNUC_MAY_ALIAS * atomic,gint val)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
g_atomic_int_add(volatile gint G_GNUC_MAY_ALIAS * atomic,gint val)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
g_atomic_int_compare_and_exchange(volatile gint G_GNUC_MAY_ALIAS * atomic,gint oldval,gint newval)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
g_atomic_pointer_compare_and_exchange(volatile gpointer G_GNUC_MAY_ALIAS * atomic,gpointer oldval,gpointer newval)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
g_atomic_pointer_compare_and_exchange(volatile gpointer G_GNUC_MAY_ALIAS * atomic,gpointer oldval,gpointer newval)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
g_atomic_pointer_compare_and_exchange(volatile gpointer G_GNUC_MAY_ALIAS * atomic,gpointer oldval,gpointer newval)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
g_atomic_pointer_compare_and_exchange(volatile gpointer G_GNUC_MAY_ALIAS * atomic,gpointer oldval,gpointer newval)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
g_atomic_pointer_compare_and_exchange(volatile gpointer G_GNUC_MAY_ALIAS * atomic,gpointer oldval,gpointer newval)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
g_atomic_int_exchange_and_add(volatile gint G_GNUC_MAY_ALIAS * atomic,gint val)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
g_atomic_int_add(volatile gint G_GNUC_MAY_ALIAS * atomic,gint val)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
g_atomic_int_compare_and_exchange(volatile gint G_GNUC_MAY_ALIAS * atomic,gint oldval,gint newval)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
g_atomic_pointer_compare_and_exchange(volatile gpointer G_GNUC_MAY_ALIAS * atomic,gpointer oldval,gpointer newval)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
g_atomic_int_exchange_and_add(volatile gint G_GNUC_MAY_ALIAS * atomic,gint val)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
g_atomic_int_add(volatile gint G_GNUC_MAY_ALIAS * atomic,gint val)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
g_atomic_int_exchange_and_add(volatile gint G_GNUC_MAY_ALIAS * atomic,gint val)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
g_atomic_int_add(volatile gint G_GNUC_MAY_ALIAS * atomic,gint val)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
g_atomic_int_compare_and_exchange(volatile gint G_GNUC_MAY_ALIAS * atomic,gint oldval,gint newval)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
g_atomic_pointer_compare_and_exchange(volatile gpointer G_GNUC_MAY_ALIAS * atomic,gpointer oldval,gpointer newval)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
g_atomic_int_compare_and_exchange(volatile gint G_GNUC_MAY_ALIAS * atomic,gint oldval,gint newval)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
g_atomic_pointer_compare_and_exchange(volatile gpointer G_GNUC_MAY_ALIAS * atomic,gpointer oldval,gpointer newval)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
g_atomic_int_exchange_and_add(volatile gint G_GNUC_MAY_ALIAS * atomic,gint val)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
g_atomic_int_add(volatile gint G_GNUC_MAY_ALIAS * atomic,gint val)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
g_atomic_int_compare_and_exchange(volatile gint G_GNUC_MAY_ALIAS * atomic,gint oldval,gint newval)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
g_atomic_pointer_compare_and_exchange(volatile gpointer G_GNUC_MAY_ALIAS * atomic,gpointer oldval,gpointer newval)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
g_atomic_pointer_compare_and_exchange(volatile gpointer G_GNUC_MAY_ALIAS * atomic,gpointer oldval,gpointer newval)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
g_atomic_pointer_compare_and_exchange(volatile gpointer G_GNUC_MAY_ALIAS * atomic,gpointer oldval,gpointer newval)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 
atomic_spin_trylock(void)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 
atomic_spin_lock(void)581 static void atomic_spin_lock (void)
582 {
583   while (atomic_spin_trylock())
584     sched_yield();
585 }
586 
atomic_spin_unlock(void)587 static void atomic_spin_unlock (void)
588 {
589   atomic_spin = 0;
590 }
591 
592 gint
g_atomic_int_exchange_and_add(volatile gint G_GNUC_MAY_ALIAS * atomic,gint val)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
g_atomic_int_add(volatile gint G_GNUC_MAY_ALIAS * atomic,gint val)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
g_atomic_int_compare_and_exchange(volatile gint G_GNUC_MAY_ALIAS * atomic,gint oldval,gint newval)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
g_atomic_pointer_compare_and_exchange(volatile gpointer G_GNUC_MAY_ALIAS * atomic,gpointer oldval,gpointer newval)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
g_atomic_pointer_compare_and_exchange(volatile gpointer G_GNUC_MAY_ALIAS * atomic,gpointer oldval,gpointer newval)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
g_atomic_int_compare_and_exchange(volatile gint G_GNUC_MAY_ALIAS * atomic,gint oldval,gint newval)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
g_atomic_int_exchange_and_add(volatile gint G_GNUC_MAY_ALIAS * atomic,gint val)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
g_atomic_int_add(volatile gint G_GNUC_MAY_ALIAS * atomic,gint val)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
g_atomic_int_exchange_and_add(volatile gint32 G_GNUC_MAY_ALIAS * atomic,gint32 val)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
g_atomic_int_add(volatile gint32 G_GNUC_MAY_ALIAS * atomic,gint32 val)807 g_atomic_int_add (volatile gint32 G_GNUC_MAY_ALIAS *atomic,
808 		  gint32           val)
809 {
810   InterlockedExchangeAdd (atomic, val);
811 }
812 
813 gboolean
g_atomic_int_compare_and_exchange(volatile gint32 G_GNUC_MAY_ALIAS * atomic,gint32 oldval,gint32 newval)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
g_atomic_pointer_compare_and_exchange(volatile gpointer G_GNUC_MAY_ALIAS * atomic,gpointer oldval,gpointer newval)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
g_atomic_int_exchange_and_add(volatile gint G_GNUC_MAY_ALIAS * atomic,gint val)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
g_atomic_int_add(volatile gint G_GNUC_MAY_ALIAS * atomic,gint val)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
g_atomic_int_compare_and_exchange(volatile gint G_GNUC_MAY_ALIAS * atomic,gint oldval,gint newval)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
g_atomic_pointer_compare_and_exchange(volatile gpointer G_GNUC_MAY_ALIAS * atomic,gpointer oldval,gpointer newval)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
gint(g_atomic_int_get)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 
gpointer(g_atomic_pointer_get)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)
gint(g_atomic_int_get)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 
gpointer(g_atomic_pointer_get)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
g_atomic_int_compare_and_exchange(volatile gint G_GNUC_MAY_ALIAS * atomic,gint oldval,gint newval)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
g_atomic_int_exchange_and_add(volatile gint G_GNUC_MAY_ALIAS * atomic,gint val)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
g_atomic_int_add(volatile gint G_GNUC_MAY_ALIAS * atomic,gint val)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
_g_atomic_thread_init(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
gint(g_atomic_int_get)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 
gpointer(g_atomic_pointer_get)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