New files to implement atomic operations for different platforms. Fixes
[platform/upstream/glib.git] / glib / gatomic.h
1 /* GLIB - Library of useful routines for C programming
2  * Copyright (C) 1995-1997  Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * GAtomic: atomic integer operation.
5  * Copyright (C) 2003 Sebastian Wilhelmi
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22  
23 /*
24  * Modified by the GLib Team and others 1997-2000.  See the AUTHORS
25  * file for a list of people on the GLib Team.  See the ChangeLog
26  * files for a list of changes.  These files are distributed with
27  * GLib at ftp://ftp.gtk.org/pub/gtk/.
28  */
29  
30 #ifndef __G_ATOMIC_H__
31 #define __G_ATOMIC_H__
32  
33 #include <glib/gtypes.h>
34
35 G_BEGIN_DECLS
36  
37 #ifdef G_THREADS_ENABLED
38
39 gint32   g_atomic_int_exchange_and_add_fallback         (gint32   *atomic, 
40                                                          gint32    val);
41 void     g_atomic_int_add_fallback                      (gint32   *atomic, 
42                                                          gint32    val);
43 gboolean g_atomic_int_compare_and_exchange_fallback     (gint32   *atomic, 
44                                                          gint32    oldval, 
45                                                          gint32    newval);
46 gboolean g_atomic_pointer_compare_and_exchange_fallback (gpointer *atomic, 
47                                                          gpointer  oldval, 
48                                                          gpointer  newval);
49
50 # if defined (__GNUC__)
51 #   if defined (G_ATOMIC_INLINED_IMPLEMENTATION_I486)
52 /* Adapted from CVS version 1.10 of glibc's sysdeps/i386/i486/bits/atomic.h 
53  */
54 static inline gint32
55 g_atomic_int_exchange_and_add (gint32 *atomic, 
56                                gint32 val)
57 {
58   gint32 result;
59
60   __asm__ __volatile__ ("lock; xaddl %0,%1"
61                         : "=r" (result), "=m" (*atomic) 
62                         : "0" (val), "m" (*atomic));
63   return result;
64 }
65  
66 static inline void
67 g_atomic_int_add (gint32 *atomic, 
68                   gint32 val)
69 {
70   __asm__ __volatile__ ("lock; addl %1,%0"
71                         : "=m" (*atomic) 
72                         : "ir" (val), "m" (*atomic));
73 }
74
75 static inline gboolean
76 g_atomic_int_compare_and_exchange (gint32 *atomic, 
77                                    gint32 oldval, 
78                                    gint32 newval)
79 {
80   gint32 result;
81  
82   __asm __volatile ("lock; cmpxchgl %2, %1"
83                     : "=a" (result), "=m" (*atomic)
84                     : "r" (newval), "m" (*atomic), "0" (oldval)); 
85
86   return result == oldval;
87 }
88
89 /* The same code as above, as on i386 gpointer is 32 bit as well.
90  * Duplicating the code here seems more natural than casting the
91  * arguments and calling the former function */
92
93 static inline gboolean
94 g_atomic_pointer_compare_and_exchange (gpointer *atomic, 
95                                        gpointer  oldval, 
96                                        gpointer  newval)
97 {
98   gpointer result;
99  
100   __asm __volatile ("lock; cmpxchgl %2, %1"
101                     : "=a" (result), "=m" (*atomic)
102                     : "r" (newval), "m" (*atomic), "0" (oldval)); 
103
104   return result == oldval;
105 }
106
107 #      define G_ATOMIC_MEMORY_BARRIER() /* Not needed */
108
109 #   elif defined(G_ATOMIC_INLINED_IMPLEMENTATION_SPARCV9) \
110    && (defined(__sparcv8) || defined(__sparcv9) || defined(__sparc_v9__))
111 /* Adapted from CVS version 1.3 of glibc's sysdeps/sparc/sparc64/bits/atomic.h
112  */
113 /* Why the test for __sparcv8, wheras really the sparcv9 architecture
114  * is required for the folowing assembler instructions? On
115  * sparc-solaris the only difference detectable at compile time
116  * between no -m and -mcpu=v9 is __sparcv8.
117  *
118  * However, in case -mcpu=v8 is set, the assembler will fail. This
119  * should be rare however, as there are only very few v8-not-v9
120  * machines still out there (and we can't do better).
121  */
122 static inline gboolean
123 g_atomic_int_compare_and_exchange (gint32 *atomic, 
124                                    gint32 oldval, 
125                                    gint32 newval)
126 {
127   gint32 result;
128   __asm __volatile ("cas [%4], %2, %0"
129                     : "=r" (result), "=m" (*atomic)
130                     : "r" (oldval), "m" (*atomic), "r" (atomic),
131                       "0" (newval));
132   return result != 0;
133 }
134
135 #     if GLIB_SIZEOF_VOID_P == 4 /* 32-bit system */
136 static inline gboolean
137 g_atomic_pointer_compare_and_exchange (gpointer *atomic, 
138                                        gpointer  oldval, 
139                                        gpointer  newval)
140 {
141   gpointer result;
142   __asm __volatile ("cas [%4], %2, %0"
143                     : "=r" (result), "=m" (*atomic)
144                     : "r" (oldval), "m" (*atomic), "r" (atomic),
145                       "0" (newval));
146   return result != 0;
147 }
148 #     elif GLIB_SIZEOF_VOID_P == 8 /* 64-bit system */
149 static inline gboolean
150 g_atomic_pointer_compare_and_exchange (gpointer *atomic, 
151                                        gpointer  oldval, 
152                                        gpointer  newval)
153 {
154   gpointer result;
155   gpointer *a = atomic;
156   __asm __volatile ("casx [%4], %2, %0"
157                     : "=r" (result), "=m" (*a)
158                     : "r" (oldval), "m" (*a), "r" (a),
159                       "0" (newval));
160   return result != 0;
161 }
162 #     else /* What's that */
163 #       error "Your system has an unsupported pointer size"
164 #     endif /* GLIB_SIZEOF_VOID_P */
165 static inline gint32
166 g_atomic_int_exchange_and_add (gint32 *atomic, 
167                                gint32 val)
168 {
169   gint32 result;
170   do
171     result = *atomic;
172   while (!g_atomic_int_compare_and_exchange (atomic, result, result + val));
173
174   return result;
175 }
176  
177 static inline void
178 g_atomic_int_add (gint32 *atomic, 
179                   gint32 val)
180 {
181   g_atomic_int_exchange_and_add (atomic, val);
182 }
183
184 #      define G_ATOMIC_MEMORY_BARRIER()                                 \
185   __asm __volatile ("membar #LoadLoad | #LoadStore"                     \
186                     " | #StoreLoad | #StoreStore" : : : "memory")
187
188 #   elif defined(G_ATOMIC_INLINED_IMPLEMENTATION_ALPHA)
189 /* Adapted from CVS version 1.3 of glibc's sysdeps/alpha/bits/atomic.h
190  */
191 static inline gboolean
192 g_atomic_int_compare_and_exchange (gint32 *atomic, 
193                                    gint32 oldval, 
194                                    gint32 newval)
195 {
196   gint32 result;
197   gint32 prev;
198   __asm__ __volatile__ (
199         "       mb\n"
200         "1:     ldl_l   %0,%2\n"
201         "       cmpeq   %0,%3,%1\n"
202         "       beq     %1,2f\n"
203         "       mov     %4,%1\n"
204         "       stl_c   %1,%2\n"
205         "       beq     %1,1b\n"
206         "       mb\n"
207         "2:"
208         : "=&r" (prev), 
209           "=&r" (result)
210         : "m" (*atomic),
211           "Ir" ((gint64)oldval),
212           "Ir" (newval)
213         : "memory");
214   return result != 0;
215 }
216 #     if GLIB_SIZEOF_VOID_P == 4 /* 32-bit system */
217 static inline gboolean
218 g_atomic_pointer_compare_and_exchange (gpointer *atomic, 
219                                        gpointer  oldval, 
220                                        gpointer  newval)
221 {
222   gint32 result;
223   gpointer prev;
224   __asm__ __volatile__ (
225         "       mb\n"
226         "1:     ldl_l   %0,%2\n"
227         "       cmpeq   %0,%3,%1\n"
228         "       beq     %1,2f\n"
229         "       mov     %4,%1\n"
230         "       stl_c   %1,%2\n"
231         "       beq     %1,1b\n"
232         "       mb\n"
233         "2:"
234         : "=&r" (prev), 
235           "=&r" (result)
236         : "m" (*atomic),
237           "Ir" ((gint64)oldval),
238           "Ir" (newval)
239         : "memory");
240   return result != 0;
241 }
242 #     elif GLIB_SIZEOF_VOID_P == 8 /* 64-bit system */
243 static inline gboolean
244 g_atomic_pointer_compare_and_exchange (gpointer *atomic, 
245                                        gpointer  oldval, 
246                                        gpointer  newval)
247 {
248   gint32 result;
249   gpointer prev;
250   __asm__ __volatile__ (
251         "       mb\n"
252         "1:     ldq_l   %0,%2\n"
253         "       cmpeq   %0,%3,%1\n"
254         "       beq     %1,2f\n"
255         "       mov     %4,%1\n"
256         "       stq_c   %1,%2\n"
257         "       beq     %1,1b\n"
258         "       mb\n"
259         "2:"
260         : "=&r" (prev), 
261           "=&r" (result)
262         : "m" (*atomic),
263           "Ir" ((gint64)oldval),
264           "Ir" (newval)
265         : "memory");
266   return result != 0;
267 }
268 #     else /* What's that */
269 #       error "Your system has an unsupported pointer size"
270 #     endif /* GLIB_SIZEOF_VOID_P */
271 static inline gint32
272 g_atomic_int_exchange_and_add (gint32 *atomic, 
273                                gint32 val)
274 {
275   gint32 result;
276   do
277     result = *atomic;
278   while (!g_atomic_int_compare_and_exchange (atomic, result, result + val));
279
280   return result;
281 }
282  
283 static inline void
284 g_atomic_int_add (gint32 *atomic, 
285                   gint32 val)
286 {
287   g_atomic_int_exchange_and_add (atomic, val);
288 }
289
290 #      define G_ATOMIC_MEMORY_BARRIER() __asm ("mb" : : : "memory")
291
292 #   elif defined(G_ATOMIC_INLINED_IMPLEMENTATION_X86_64)
293 /* Adapted from CVS version 1.9 of glibc's sysdeps/x86_64/bits/atomic.h 
294  */
295 static inline gint32
296 g_atomic_int_exchange_and_add (gint32 *atomic, 
297                                gint32 val)
298 {
299   gint32 result;
300
301   __asm__ __volatile__ ("lock; xaddl %0,%1"
302                         : "=r" (result), "=m" (*atomic) 
303                         : "0" (val), "m" (*atomic));
304   return result;
305 }
306  
307 static inline void
308 g_atomic_int_add (gint32 *atomic, 
309                   gint32 val)
310 {
311   __asm__ __volatile__ ("lock; addl %1,%0"
312                         : "=m" (*atomic) 
313                         : "ir" (val), "m" (*atomic));
314 }
315
316 static inline gboolean
317 g_atomic_int_compare_and_exchange (gint32 *atomic, 
318                                    gint32 oldval, 
319                                    gint32 newval)
320 {
321   gint32 result;
322  
323   __asm __volatile ("lock; cmpxchgl %2, %1"
324                     : "=a" (result), "=m" (*atomic)
325                     : "r" (newval), "m" (*atomic), "0" (oldval)); 
326
327   return result == oldval;
328 }
329
330 static inline gboolean
331 g_atomic_pointer_compare_and_exchange (gpointer *atomic, 
332                                    gpointer  oldval, 
333                                    gpointer  newval)
334 {
335   gpointer result;
336  
337   __asm __volatile ("lock; cmpxchgq %q2, %1"
338                     : "=a" (result), "=m" (*atomic)
339                     : "r" (newval), "m" (*atomic), "0" (oldval)); 
340
341   return result == oldval;
342 }
343
344 #     define  G_ATOMIC_MEMORY_BARRIER() /* Not needed */
345
346 #   elif defined(G_ATOMIC_INLINED_IMPLEMENTATION_POWERPC)
347 /* Adapted from CVS version 1.12 of glibc's sysdeps/powerpc/bits/atomic.h 
348  * and CVS version 1.3 of glibc's sysdeps/powerpc/powerpc32/bits/atomic.h 
349  * and CVS version 1.2 of glibc's sysdeps/powerpc/powerpc64/bits/atomic.h 
350  */
351 static inline gint32
352 g_atomic_int_exchange_and_add (gint32 *atomic, 
353                           gint32 val)
354 {
355   gint32 result, temp;
356   __asm __volatile ("1:       lwarx   %0,0,%3\n"
357                     "         add     %1,%0,%4\n"
358                     "         stwcx.  %1,0,%3\n"
359                     "         bne-    1b"
360                     : "=&b" (result), "=&r" (temp), "=m" (*atomic)
361                     : "b" (atomic), "r" (val), "2" (*atomic)
362                     : "cr0", "memory");
363   return result;
364 }
365  
366 static inline void
367 g_atomic_int_add (gint32 *atomic, 
368                   gint32 val)
369 {
370   g_atomic_int_exchange_and_add (atomic, val);
371 }
372
373 #     if GLIB_SIZEOF_VOID_P == 4 /* 32-bit system */
374 static inline gboolean
375 g_atomic_int_compare_and_exchange (gint32 *atomic, 
376                                gint32 oldval, 
377                                gint32 newval)
378 {
379   gint32 result;
380   __asm __volatile ("sync\n"
381                     "1: lwarx   %0,0,%1\n"
382                     "   subf.   %0,%2,%0\n"
383                     "   bne     2f\n"
384                     "   stwcx.  %3,0,%1\n"
385                     "   bne-    1b\n"
386                     "2: isync"
387                     : "=&r" (result)
388                     : "b" (atomic), "r" (oldval), "r" (newval)
389                     : "cr0", "memory"); 
390   return result == 0;
391 }
392
393 static inline gboolean
394 g_atomic_pointer_compare_and_exchange (gpointer *atomic, 
395                                    gpointer  oldval, 
396                                    gpointer  newval)
397 {
398   gpointer result;
399   __asm __volatile ("sync\n"
400                     "1: lwarx   %0,0,%1\n"
401                     "   subf.   %0,%2,%0\n"
402                     "   bne     2f\n"
403                     "   stwcx.  %3,0,%1\n"
404                     "   bne-    1b\n"
405                     "2: isync"
406                     : "=&r" (result)
407                     : "b" (atomic), "r" (oldval), "r" (newval)
408                     : "cr0", "memory"); 
409   return result == 0;
410 }
411 #     elif GLIB_SIZEOF_VOID_P == 8 /* 64-bit system */
412 static inline gboolean
413 g_atomic_int_compare_and_exchange (gint32 *atomic, 
414                                gint32 oldval, 
415                                gint32 newval)
416 {
417   __asm __volatile ("sync\n"
418                     "1: lwarx   %0,0,%1\n"
419                     "   extsw   %0,%0\n"
420                     "   subf.   %0,%2,%0\n"
421                     "   bne     2f\n"
422                     "   stwcx.  %3,0,%1\n"
423                     "   bne-    1b\n"
424                     "2: isync"
425                     : "=&r" (result)
426                     : "b" (atomic), "r" (oldval), "r" (newval)
427                     : "cr0", "memory"); 
428   return result == 0;
429 }
430
431 static inline gboolean
432 g_atomic_pointer_compare_and_exchange (gpointer *atomic, 
433                                    gpointer  oldval, 
434                                    gpointer  newval)
435 {
436   gpointer result;
437   __asm __volatile ("sync\n"
438                     "1: ldarx   %0,0,%1\n"
439                     "   subf.   %0,%2,%0\n"
440                     "   bne     2f\n"
441                     "   stdcx.  %3,0,%1\n"
442                     "   bne-    1b\n"
443                     "2: isync"
444                     : "=&r" (result)
445                     : "b" (atomic), "r" (oldval), "r" (newval)
446                     : "cr0", "memory"); 
447   return result == 0;
448 }
449 #     else /* What's that */
450 #       error "Your system has an unsupported pointer size"
451 #     endif /* GLIB_SIZEOF_VOID_P */
452
453 #     define  G_ATOMIC_MEMORY_BARRIER() __asm ("sync" : : : "memory")
454
455 #   elif defined(G_ATOMIC_INLINED_IMPLEMENTATION_IA64)
456 /* Adapted from CVS version 1.8 of glibc's sysdeps/ia64/bits/atomic.h
457  */
458 static inline gint32
459 g_atomic_int_exchange_and_add (gint32 *atomic, 
460                           gint32 val)
461 {
462   return __sync_fetch_and_add_si (atomic, val);
463 }
464  
465 static inline void
466 g_atomic_int_add (gint32 *atomic, 
467                   gint32 val)
468 {
469   __sync_fetch_and_add_si (atomic, val);
470 }
471
472 static inline gboolean
473 g_atomic_int_compare_and_exchange (gint32 *atomic, 
474                                gint32 oldval, 
475                                gint32 newval)
476 {
477   return __sync_bool_compare_and_exchange_si (atomic, oldval, newval);
478 }
479
480 static inline gboolean
481 g_atomic_pointer_compare_and_exchange (gpointer *atomic, 
482                                    gpointer  oldval, 
483                                    gpointer  newval)
484 {
485   return __sync_bool_compare_and_exchange_di ((long *)atomic, 
486                                           (long)oldval, (long)newval);
487 }
488
489 #     define  G_ATOMIC_MEMORY_BARRIER() __sync_synchronize ()
490
491 #   else /* !G_ATOMIC_INLINED_IMPLEMENTATION_... */
492 #     define G_ATOMIC_USE_FALLBACK_IMPLEMENTATION
493 #   endif /* G_ATOMIC_INLINED_IMPLEMENTATION_... */
494 # else /* !__GNU__ */
495 #   define G_ATOMIC_USE_FALLBACK_IMPLEMENTATION
496 # endif /* __GNUC__ */
497 #else /* !G_THREADS_ENABLED */
498 gint32 g_atomic_int_exchange_and_add (gint32 *atomic, gint32  val);
499 # define g_atomic_int_add(atomic, val) (void)(*(atomic) += (val))
500 # define g_atomic_int_compare_and_exchange(atomic, oldval, newval)              \
501   (*(atomic) == (oldval) ? (*(atomic) = (newval), TRUE) : FALSE)
502 # define g_atomic_pointer_compare_and_exchange(atomic, oldval, newval)  \
503   (*(atomic) == (oldval) ? (*(atomic) = (newval), TRUE) : FALSE)
504 # define g_atomic_int_get(atomic) (*(atomic))
505 # define g_atomic_pointer_get(atomic) (*(atomic))
506 #endif /* G_THREADS_ENABLED */
507
508 #ifdef G_ATOMIC_USE_FALLBACK_IMPLEMENTATION
509 # define g_atomic_int_exchange_and_add                                  \
510     g_atomic_int_exchange_and_add_fallback
511 # define g_atomic_int_add                                               \
512     g_atomic_int_add_fallback
513 # define g_atomic_int_compare_and_exchange                                      \
514     g_atomic_int_compare_and_exchange_fallback
515 # define g_atomic_pointer_compare_and_exchange                          \
516     g_atomic_pointer_compare_and_exchange_fallback
517 # define g_atomic_int_get                                               \
518     g_atomic_int_get_fallback
519 # define g_atomic_pointer_get                                           \
520     g_atomic_pointer_get_fallback
521 #else /* !G_ATOMIC_USE_FALLBACK_IMPLEMENTATION */
522 static inline gint32
523 g_atomic_int_get (gint32 *atomic)
524 {
525   gint32 result = *atomic;
526   G_ATOMIC_MEMORY_BARRIER ();
527   return result;
528 }
529
530 static inline gpointer
531 g_atomic_pointer_get (gpointer *atomic)
532 {
533   gpointer result = *atomic;
534   G_ATOMIC_MEMORY_BARRIER ();
535   return result;
536 }   
537 #endif /* G_ATOMIC_USE_FALLBACK_IMPLEMENTATION */
538
539 #define g_atomic_int_inc(atomic) (g_atomic_int_add ((atomic), 1))
540 #define g_atomic_int_dec_and_test(atomic)                               \
541   (g_atomic_int_exchange_and_add ((atomic), -1) == 1)
542
543 G_END_DECLS
544  
545 #endif /* __G_ATOMIC_H__ */