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