source: trunk/third/glib2/glib/gatomic.c @ 20721

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