| /* GLIB - Library of useful routines for C programming |
| * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald |
| * |
| * g_atomic_*: atomic operations. |
| * Copyright (C) 2003 Sebastian Wilhelmi |
| * Copyright (C) 2007 Nokia Corporation |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the |
| * Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
| * Boston, MA 02111-1307, USA. |
| */ |
| |
| #include "config.h" |
| |
| #if defined (G_ATOMIC_ARM) |
| #include <sched.h> |
| #endif |
| |
| #include "gatomic.h" |
| #include "gthreadprivate.h" |
| |
| /** |
| * SECTION:atomic_operations |
| * @title: Atomic Operations |
| * @short_description: basic atomic integer and pointer operations |
| * @see_also: #GMutex |
| * |
| * The following functions can be used to atomically access integers and |
| * pointers. They are implemented as inline assembler function on most |
| * platforms and use slower fall-backs otherwise. Using them can sometimes |
| * save you from using a performance-expensive #GMutex to protect the |
| * integer or pointer. |
| * |
| * The most important usage is reference counting. Using |
| * g_atomic_int_inc() and g_atomic_int_dec_and_test() makes reference |
| * counting a very fast operation. |
| * |
| * <note><para>You must not directly read integers or pointers concurrently |
| * accessed by multiple threads, but use the atomic accessor functions |
| * instead. That is, always use g_atomic_int_get() and g_atomic_pointer_get() |
| * for read outs. They provide the neccessary synchonization mechanisms |
| * like memory barriers to access memory locations concurrently. |
| * </para></note> |
| * |
| * <note><para>If you are using those functions for anything apart from |
| * simple reference counting, you should really be aware of the implications |
| * of doing that. There are literally thousands of ways to shoot yourself |
| * in the foot. So if in doubt, use a #GMutex. If you don't know, what |
| * memory barriers are, do not use anything but g_atomic_int_inc() and |
| * g_atomic_int_dec_and_test(). |
| * </para></note> |
| * |
| * <note><para>It is not safe to set an integer or pointer just by assigning |
| * to it, when it is concurrently accessed by other threads with the following |
| * functions. Use g_atomic_int_compare_and_exchange() or |
| * g_atomic_pointer_compare_and_exchange() respectively. |
| * </para></note> |
| */ |
| |
| #if defined (__GNUC__) |
| # if defined (G_ATOMIC_I486) |
| /* Adapted from CVS version 1.10 of glibc's sysdeps/i386/i486/bits/atomic.h |
| */ |
| gint |
| g_atomic_int_exchange_and_add (volatile gint G_GNUC_MAY_ALIAS *atomic, |
| gint val) |
| { |
| gint result; |
| |
| __asm__ __volatile__ ("lock; xaddl %0,%1" |
| : "=r" (result), "=m" (*atomic) |
| : "0" (val), "m" (*atomic)); |
| return result; |
| } |
| |
| void |
| g_atomic_int_add (volatile gint G_GNUC_MAY_ALIAS *atomic, |
| gint val) |
| { |
| __asm__ __volatile__ ("lock; addl %1,%0" |
| : "=m" (*atomic) |
| : "ir" (val), "m" (*atomic)); |
| } |
| |
| gboolean |
| g_atomic_int_compare_and_exchange (volatile gint G_GNUC_MAY_ALIAS *atomic, |
| gint oldval, |
| gint newval) |
| { |
| gint result; |
| |
| __asm__ __volatile__ ("lock; cmpxchgl %2, %1" |
| : "=a" (result), "=m" (*atomic) |
| : "r" (newval), "m" (*atomic), "0" (oldval)); |
| |
| return result == oldval; |
| } |
| |
| /* The same code as above, as on i386 gpointer is 32 bit as well. |
| * Duplicating the code here seems more natural than casting the |
| * arguments and calling the former function */ |
| |
| gboolean |
| g_atomic_pointer_compare_and_exchange (volatile gpointer G_GNUC_MAY_ALIAS *atomic, |
| gpointer oldval, |
| gpointer newval) |
| { |
| gpointer result; |
| |
| __asm__ __volatile__ ("lock; cmpxchgl %2, %1" |
| : "=a" (result), "=m" (*atomic) |
| : "r" (newval), "m" (*atomic), "0" (oldval)); |
| |
| return result == oldval; |
| } |
| |
| # elif defined (G_ATOMIC_SPARCV9) |
| /* Adapted from CVS version 1.3 of glibc's sysdeps/sparc/sparc64/bits/atomic.h |
| */ |
| # define ATOMIC_INT_CMP_XCHG(atomic, oldval, newval) \ |
| ({ \ |
| gint __result; \ |
| __asm__ __volatile__ ("cas [%4], %2, %0" \ |
| : "=r" (__result), "=m" (*(atomic)) \ |
| : "r" (oldval), "m" (*(atomic)), "r" (atomic),\ |
| "0" (newval)); \ |
| __result == oldval; \ |
| }) |
| |
| # if GLIB_SIZEOF_VOID_P == 4 /* 32-bit system */ |
| gboolean |
| g_atomic_pointer_compare_and_exchange (volatile gpointer G_GNUC_MAY_ALIAS *atomic, |
| gpointer oldval, |
| gpointer newval) |
| { |
| gpointer result; |
| __asm__ __volatile__ ("cas [%4], %2, %0" |
| : "=r" (result), "=m" (*atomic) |
| : "r" (oldval), "m" (*atomic), "r" (atomic), |
| "0" (newval)); |
| return result == oldval; |
| } |
| # elif GLIB_SIZEOF_VOID_P == 8 /* 64-bit system */ |
| gboolean |
| g_atomic_pointer_compare_and_exchange (volatile gpointer G_GNUC_MAY_ALIAS *atomic, |
| gpointer oldval, |
| gpointer newval) |
| { |
| gpointer result; |
| gpointer *a = atomic; |
| __asm__ __volatile__ ("casx [%4], %2, %0" |
| : "=r" (result), "=m" (*a) |
| : "r" (oldval), "m" (*a), "r" (a), |
| "0" (newval)); |
| return result == oldval; |
| } |
| # else /* What's that */ |
| # error "Your system has an unsupported pointer size" |
| # endif /* GLIB_SIZEOF_VOID_P */ |
| # define G_ATOMIC_MEMORY_BARRIER \ |
| __asm__ __volatile__ ("membar #LoadLoad | #LoadStore" \ |
| " | #StoreLoad | #StoreStore" : : : "memory") |
| |
| # elif defined (G_ATOMIC_ALPHA) |
| /* Adapted from CVS version 1.3 of glibc's sysdeps/alpha/bits/atomic.h |
| */ |
| # define ATOMIC_INT_CMP_XCHG(atomic, oldval, newval) \ |
| ({ \ |
| gint __result; \ |
| gint __prev; \ |
| __asm__ __volatile__ ( \ |
| " mb\n" \ |
| "1: ldl_l %0,%2\n" \ |
| " cmpeq %0,%3,%1\n" \ |
| " beq %1,2f\n" \ |
| " mov %4,%1\n" \ |
| " stl_c %1,%2\n" \ |
| " beq %1,1b\n" \ |
| " mb\n" \ |
| "2:" \ |
| : "=&r" (__prev), \ |
| "=&r" (__result) \ |
| : "m" (*(atomic)), \ |
| "Ir" (oldval), \ |
| "Ir" (newval) \ |
| : "memory"); \ |
| __result != 0; \ |
| }) |
| # if GLIB_SIZEOF_VOID_P == 4 /* 32-bit system */ |
| gboolean |
| g_atomic_pointer_compare_and_exchange (volatile gpointer G_GNUC_MAY_ALIAS *atomic, |
| gpointer oldval, |
| gpointer newval) |
| { |
| gint result; |
| gpointer prev; |
| __asm__ __volatile__ ( |
| " mb\n" |
| "1: ldl_l %0,%2\n" |
| " cmpeq %0,%3,%1\n" |
| " beq %1,2f\n" |
| " mov %4,%1\n" |
| " stl_c %1,%2\n" |
| " beq %1,1b\n" |
| " mb\n" |
| "2:" |
| : "=&r" (prev), |
| "=&r" (result) |
| : "m" (*atomic), |
| "Ir" (oldval), |
| "Ir" (newval) |
| : "memory"); |
| return result != 0; |
| } |
| # elif GLIB_SIZEOF_VOID_P == 8 /* 64-bit system */ |
| gboolean |
| g_atomic_pointer_compare_and_exchange (volatile gpointer G_GNUC_MAY_ALIAS *atomic, |
| gpointer oldval, |
| gpointer newval) |
| { |
| gint result; |
| gpointer prev; |
| __asm__ __volatile__ ( |
| " mb\n" |
| "1: ldq_l %0,%2\n" |
| " cmpeq %0,%3,%1\n" |
| " beq %1,2f\n" |
| " mov %4,%1\n" |
| " stq_c %1,%2\n" |
| " beq %1,1b\n" |
| " mb\n" |
| "2:" |
| : "=&r" (prev), |
| "=&r" (result) |
| : "m" (*atomic), |
| "Ir" (oldval), |
| "Ir" (newval) |
| : "memory"); |
| return result != 0; |
| } |
| # else /* What's that */ |
| # error "Your system has an unsupported pointer size" |
| # endif /* GLIB_SIZEOF_VOID_P */ |
| # define G_ATOMIC_MEMORY_BARRIER __asm__ ("mb" : : : "memory") |
| # elif defined (G_ATOMIC_X86_64) |
| /* Adapted from CVS version 1.9 of glibc's sysdeps/x86_64/bits/atomic.h |
| */ |
| gint |
| g_atomic_int_exchange_and_add (volatile gint G_GNUC_MAY_ALIAS *atomic, |
| gint val) |
| { |
| gint result; |
| |
| __asm__ __volatile__ ("lock; xaddl %0,%1" |
| : "=r" (result), "=m" (*atomic) |
| : "0" (val), "m" (*atomic)); |
| return result; |
| } |
| |
| void |
| g_atomic_int_add (volatile gint G_GNUC_MAY_ALIAS *atomic, |
| gint val) |
| { |
| __asm__ __volatile__ ("lock; addl %1,%0" |
| : "=m" (*atomic) |
| : "ir" (val), "m" (*atomic)); |
| } |
| |
| gboolean |
| g_atomic_int_compare_and_exchange (volatile gint G_GNUC_MAY_ALIAS *atomic, |
| gint oldval, |
| gint newval) |
| { |
| gint result; |
| |
| __asm__ __volatile__ ("lock; cmpxchgl %2, %1" |
| : "=a" (result), "=m" (*atomic) |
| : "r" (newval), "m" (*atomic), "0" (oldval)); |
| |
| return result == oldval; |
| } |
| |
| gboolean |
| g_atomic_pointer_compare_and_exchange (volatile gpointer G_GNUC_MAY_ALIAS *atomic, |
| gpointer oldval, |
| gpointer newval) |
| { |
| gpointer result; |
| |
| __asm__ __volatile__ ("lock; cmpxchgq %q2, %1" |
| : "=a" (result), "=m" (*atomic) |
| : "r" (newval), "m" (*atomic), "0" (oldval)); |
| |
| return result == oldval; |
| } |
| |
| # elif defined (G_ATOMIC_POWERPC) |
| /* Adapted from CVS version 1.16 of glibc's sysdeps/powerpc/bits/atomic.h |
| * and CVS version 1.4 of glibc's sysdeps/powerpc/powerpc32/bits/atomic.h |
| * and CVS version 1.7 of glibc's sysdeps/powerpc/powerpc64/bits/atomic.h |
| */ |
| # ifdef __OPTIMIZE__ |
| /* Non-optimizing compile bails on the following two asm statements |
| * for reasons unknown to the author */ |
| gint |
| g_atomic_int_exchange_and_add (volatile gint G_GNUC_MAY_ALIAS *atomic, |
| gint val) |
| { |
| gint result, temp; |
| #if ASM_NUMERIC_LABELS |
| __asm__ __volatile__ ("1: lwarx %0,0,%3\n" |
| " add %1,%0,%4\n" |
| " stwcx. %1,0,%3\n" |
| " bne- 1b" |
| : "=&b" (result), "=&r" (temp), "=m" (*atomic) |
| : "b" (atomic), "r" (val), "m" (*atomic) |
| : "cr0", "memory"); |
| #else |
| __asm__ __volatile__ (".Lieaa%=: lwarx %0,0,%3\n" |
| " add %1,%0,%4\n" |
| " stwcx. %1,0,%3\n" |
| " bne- .Lieaa%=" |
| : "=&b" (result), "=&r" (temp), "=m" (*atomic) |
| : "b" (atomic), "r" (val), "m" (*atomic) |
| : "cr0", "memory"); |
| #endif |
| return result; |
| } |
| |
| /* The same as above, to save a function call repeated here */ |
| void |
| g_atomic_int_add (volatile gint G_GNUC_MAY_ALIAS *atomic, |
| gint val) |
| { |
| gint result, temp; |
| #if ASM_NUMERIC_LABELS |
| __asm__ __volatile__ ("1: lwarx %0,0,%3\n" |
| " add %1,%0,%4\n" |
| " stwcx. %1,0,%3\n" |
| " bne- 1b" |
| : "=&b" (result), "=&r" (temp), "=m" (*atomic) |
| : "b" (atomic), "r" (val), "m" (*atomic) |
| : "cr0", "memory"); |
| #else |
| __asm__ __volatile__ (".Lia%=: lwarx %0,0,%3\n" |
| " add %1,%0,%4\n" |
| " stwcx. %1,0,%3\n" |
| " bne- .Lia%=" |
| : "=&b" (result), "=&r" (temp), "=m" (*atomic) |
| : "b" (atomic), "r" (val), "m" (*atomic) |
| : "cr0", "memory"); |
| #endif |
| } |
| # else /* !__OPTIMIZE__ */ |
| gint |
| g_atomic_int_exchange_and_add (volatile gint G_GNUC_MAY_ALIAS *atomic, |
| gint val) |
| { |
| gint result; |
| do |
| result = *atomic; |
| while (!g_atomic_int_compare_and_exchange (atomic, result, result + val)); |
| |
| return result; |
| } |
| |
| void |
| g_atomic_int_add (volatile gint G_GNUC_MAY_ALIAS *atomic, |
| gint val) |
| { |
| gint result; |
| do |
| result = *atomic; |
| while (!g_atomic_int_compare_and_exchange (atomic, result, result + val)); |
| } |
| # endif /* !__OPTIMIZE__ */ |
| |
| # if GLIB_SIZEOF_VOID_P == 4 /* 32-bit system */ |
| gboolean |
| g_atomic_int_compare_and_exchange (volatile gint G_GNUC_MAY_ALIAS *atomic, |
| gint oldval, |
| gint newval) |
| { |
| gint result; |
| #if ASM_NUMERIC_LABELS |
| __asm__ __volatile__ ("sync\n" |
| "1: lwarx %0,0,%1\n" |
| " subf. %0,%2,%0\n" |
| " bne 2f\n" |
| " stwcx. %3,0,%1\n" |
| " bne- 1b\n" |
| "2: isync" |
| : "=&r" (result) |
| : "b" (atomic), "r" (oldval), "r" (newval) |
| : "cr0", "memory"); |
| #else |
| __asm__ __volatile__ ("sync\n" |
| ".L1icae%=: lwarx %0,0,%1\n" |
| " subf. %0,%2,%0\n" |
| " bne .L2icae%=\n" |
| " stwcx. %3,0,%1\n" |
| " bne- .L1icae%=\n" |
| ".L2icae%=: isync" |
| : "=&r" (result) |
| : "b" (atomic), "r" (oldval), "r" (newval) |
| : "cr0", "memory"); |
| #endif |
| return result == 0; |
| } |
| |
| gboolean |
| g_atomic_pointer_compare_and_exchange (volatile gpointer G_GNUC_MAY_ALIAS *atomic, |
| gpointer oldval, |
| gpointer newval) |
| { |
| gpointer result; |
| #if ASM_NUMERIC_LABELS |
| __asm__ __volatile__ ("sync\n" |
| "1: lwarx %0,0,%1\n" |
| " subf. %0,%2,%0\n" |
| " bne 2f\n" |
| " stwcx. %3,0,%1\n" |
| " bne- 1b\n" |
| "2: isync" |
| : "=&r" (result) |
| : "b" (atomic), "r" (oldval), "r" (newval) |
| : "cr0", "memory"); |
| #else |
| __asm__ __volatile__ ("sync\n" |
| ".L1pcae%=: lwarx %0,0,%1\n" |
| " subf. %0,%2,%0\n" |
| " bne .L2pcae%=\n" |
| " stwcx. %3,0,%1\n" |
| " bne- .L1pcae%=\n" |
| ".L2pcae%=: isync" |
| : "=&r" (result) |
| : "b" (atomic), "r" (oldval), "r" (newval) |
| : "cr0", "memory"); |
| #endif |
| return result == 0; |
| } |
| # elif GLIB_SIZEOF_VOID_P == 8 /* 64-bit system */ |
| gboolean |
| g_atomic_int_compare_and_exchange (volatile gint G_GNUC_MAY_ALIAS *atomic, |
| gint oldval, |
| gint newval) |
| { |
| gpointer result; |
| #if ASM_NUMERIC_LABELS |
| __asm__ __volatile__ ("sync\n" |
| "1: lwarx %0,0,%1\n" |
| " extsw %0,%0\n" |
| " subf. %0,%2,%0\n" |
| " bne 2f\n" |
| " stwcx. %3,0,%1\n" |
| " bne- 1b\n" |
| "2: isync" |
| : "=&r" (result) |
| : "b" (atomic), "r" (oldval), "r" (newval) |
| : "cr0", "memory"); |
| #else |
| __asm__ __volatile__ ("sync\n" |
| ".L1icae%=: lwarx %0,0,%1\n" |
| " extsw %0,%0\n" |
| " subf. %0,%2,%0\n" |
| " bne .L2icae%=\n" |
| " stwcx. %3,0,%1\n" |
| " bne- .L1icae%=\n" |
| ".L2icae%=: isync" |
| : "=&r" (result) |
| : "b" (atomic), "r" (oldval), "r" (newval) |
| : "cr0", "memory"); |
| #endif |
| return result == 0; |
| } |
| |
| gboolean |
| g_atomic_pointer_compare_and_exchange (volatile gpointer G_GNUC_MAY_ALIAS *atomic, |
| gpointer oldval, |
| gpointer newval) |
| { |
| gpointer result; |
| #if ASM_NUMERIC_LABELS |
| __asm__ __volatile__ ("sync\n" |
| "1: ldarx %0,0,%1\n" |
| " subf. %0,%2,%0\n" |
| " bne 2f\n" |
| " stdcx. %3,0,%1\n" |
| " bne- 1b\n" |
| "2: isync" |
| : "=&r" (result) |
| : "b" (atomic), "r" (oldval), "r" (newval) |
| : "cr0", "memory"); |
| #else |
| __asm__ __volatile__ ("sync\n" |
| ".L1pcae%=: ldarx %0,0,%1\n" |
| " subf. %0,%2,%0\n" |
| " bne .L2pcae%=\n" |
| " stdcx. %3,0,%1\n" |
| " bne- .L1pcae%=\n" |
| ".L2pcae%=: isync" |
| : "=&r" (result) |
| : "b" (atomic), "r" (oldval), "r" (newval) |
| : "cr0", "memory"); |
| #endif |
| return result == 0; |
| } |
| # else /* What's that */ |
| # error "Your system has an unsupported pointer size" |
| # endif /* GLIB_SIZEOF_VOID_P */ |
| |
| # define G_ATOMIC_MEMORY_BARRIER __asm__ ("sync" : : : "memory") |
| |
| # elif defined (G_ATOMIC_IA64) |
| /* Adapted from CVS version 1.8 of glibc's sysdeps/ia64/bits/atomic.h |
| */ |
| gint |
| g_atomic_int_exchange_and_add (volatile gint G_GNUC_MAY_ALIAS *atomic, |
| gint val) |
| { |
| return __sync_fetch_and_add (atomic, val); |
| } |
| |
| void |
| g_atomic_int_add (volatile gint G_GNUC_MAY_ALIAS *atomic, |
| gint val) |
| { |
| __sync_fetch_and_add (atomic, val); |
| } |
| |
| gboolean |
| g_atomic_int_compare_and_exchange (volatile gint G_GNUC_MAY_ALIAS *atomic, |
| gint oldval, |
| gint newval) |
| { |
| return __sync_bool_compare_and_swap (atomic, oldval, newval); |
| } |
| |
| gboolean |
| g_atomic_pointer_compare_and_exchange (volatile gpointer G_GNUC_MAY_ALIAS *atomic, |
| gpointer oldval, |
| gpointer newval) |
| { |
| return __sync_bool_compare_and_swap ((long *)atomic, |
| (long)oldval, (long)newval); |
| } |
| |
| # define G_ATOMIC_MEMORY_BARRIER __sync_synchronize () |
| # elif defined (G_ATOMIC_S390) |
| /* Adapted from glibc's sysdeps/s390/bits/atomic.h |
| */ |
| # define ATOMIC_INT_CMP_XCHG(atomic, oldval, newval) \ |
| ({ \ |
| gint __result = oldval; \ |
| __asm__ __volatile__ ("cs %0, %2, %1" \ |
| : "+d" (__result), "=Q" (*(atomic)) \ |
| : "d" (newval), "m" (*(atomic)) : "cc" ); \ |
| __result == oldval; \ |
| }) |
| |
| # if GLIB_SIZEOF_VOID_P == 4 /* 32-bit system */ |
| gboolean |
| g_atomic_pointer_compare_and_exchange (volatile gpointer G_GNUC_MAY_ALIAS *atomic, |
| gpointer oldval, |
| gpointer newval) |
| { |
| gpointer result = oldval; |
| __asm__ __volatile__ ("cs %0, %2, %1" |
| : "+d" (result), "=Q" (*(atomic)) |
| : "d" (newval), "m" (*(atomic)) : "cc" ); |
| return result == oldval; |
| } |
| # elif GLIB_SIZEOF_VOID_P == 8 /* 64-bit system */ |
| gboolean |
| g_atomic_pointer_compare_and_exchange (volatile gpointer G_GNUC_MAY_ALIAS *atomic, |
| gpointer oldval, |
| gpointer newval) |
| { |
| gpointer result = oldval; |
| gpointer *a = atomic; |
| __asm__ __volatile__ ("csg %0, %2, %1" |
| : "+d" (result), "=Q" (*a) |
| : "d" ((long)(newval)), "m" (*a) : "cc" ); |
| return result == oldval; |
| } |
| # else /* What's that */ |
| # error "Your system has an unsupported pointer size" |
| # endif /* GLIB_SIZEOF_VOID_P */ |
| # elif defined (G_ATOMIC_ARM) |
| static volatile int atomic_spin = 0; |
| |
| static int atomic_spin_trylock (void) |
| { |
| int result; |
| |
| asm volatile ( |
| "swp %0, %1, [%2]\n" |
| : "=&r,&r" (result) |
| : "r,0" (1), "r,r" (&atomic_spin) |
| : "memory"); |
| if (result == 0) |
| return 0; |
| else |
| return -1; |
| } |
| |
| static void atomic_spin_lock (void) |
| { |
| while (atomic_spin_trylock()) |
| sched_yield(); |
| } |
| |
| static void atomic_spin_unlock (void) |
| { |
| atomic_spin = 0; |
| } |
| |
| gint |
| g_atomic_int_exchange_and_add (volatile gint G_GNUC_MAY_ALIAS *atomic, |
| gint val) |
| { |
| gint result; |
| |
| atomic_spin_lock(); |
| result = *atomic; |
| *atomic += val; |
| atomic_spin_unlock(); |
| |
| return result; |
| } |
| |
| void |
| g_atomic_int_add (volatile gint G_GNUC_MAY_ALIAS *atomic, |
| gint val) |
| { |
| atomic_spin_lock(); |
| *atomic += val; |
| atomic_spin_unlock(); |
| } |
| |
| gboolean |
| g_atomic_int_compare_and_exchange (volatile gint G_GNUC_MAY_ALIAS *atomic, |
| gint oldval, |
| gint newval) |
| { |
| gboolean result; |
| |
| atomic_spin_lock(); |
| if (*atomic == oldval) |
| { |
| result = TRUE; |
| *atomic = newval; |
| } |
| else |
| result = FALSE; |
| atomic_spin_unlock(); |
| |
| return result; |
| } |
| |
| gboolean |
| g_atomic_pointer_compare_and_exchange (volatile gpointer G_GNUC_MAY_ALIAS *atomic, |
| gpointer oldval, |
| gpointer newval) |
| { |
| gboolean result; |
| |
| atomic_spin_lock(); |
| if (*atomic == oldval) |
| { |
| result = TRUE; |
| *atomic = newval; |
| } |
| else |
| result = FALSE; |
| atomic_spin_unlock(); |
| |
| return result; |
| } |
| # elif defined (G_ATOMIC_CRIS) || defined (G_ATOMIC_CRISV32) |
| # ifdef G_ATOMIC_CRIS |
| # define CRIS_ATOMIC_INT_CMP_XCHG(atomic, oldval, newval) \ |
| ({ \ |
| gboolean __result; \ |
| __asm__ __volatile__ ("\n" \ |
| "0:\tclearf\n\t" \ |
| "cmp.d [%[Atomic]], %[OldVal]\n\t" \ |
| "bne 1f\n\t" \ |
| "ax\n\t" \ |
| "move.d %[NewVal], [%[Atomic]]\n\t" \ |
| "bwf 0b\n" \ |
| "1:\tseq %[Result]" \ |
| : [Result] "=&r" (__result), \ |
| "=m" (*(atomic)) \ |
| : [Atomic] "r" (atomic), \ |
| [OldVal] "r" (oldval), \ |
| [NewVal] "r" (newval), \ |
| "g" (*(gpointer*) (atomic)) \ |
| : "memory"); \ |
| __result; \ |
| }) |
| # else |
| # define CRIS_ATOMIC_INT_CMP_XCHG(atomic, oldval, newval) \ |
| ({ \ |
| gboolean __result; \ |
| __asm__ __volatile__ ("\n" \ |
| "0:\tclearf p\n\t" \ |
| "cmp.d [%[Atomic]], %[OldVal]\n\t" \ |
| "bne 1f\n\t" \ |
| "ax\n\t" \ |
| "move.d %[NewVal], [%[Atomic]]\n\t" \ |
| "bcs 0b\n" \ |
| "1:\tseq %[Result]" \ |
| : [Result] "=&r" (__result), \ |
| "=m" (*(atomic)) \ |
| : [Atomic] "r" (atomic), \ |
| [OldVal] "r" (oldval), \ |
| [NewVal] "r" (newval), \ |
| "g" (*(gpointer*) (atomic)) \ |
| : "memory"); \ |
| __result; \ |
| }) |
| # endif |
| |
| #define CRIS_CACHELINE_SIZE 32 |
| #define CRIS_ATOMIC_BREAKS_CACHELINE(atomic) \ |
| (((gulong)(atomic) & (CRIS_CACHELINE_SIZE - 1)) > (CRIS_CACHELINE_SIZE - sizeof (atomic))) |
| |
| gint __g_atomic_int_exchange_and_add (volatile gint G_GNUC_MAY_ALIAS *atomic, |
| gint val); |
| void __g_atomic_int_add (volatile gint G_GNUC_MAY_ALIAS *atomic, |
| gint val); |
| gboolean __g_atomic_int_compare_and_exchange (volatile gint G_GNUC_MAY_ALIAS *atomic, |
| gint oldval, |
| gint newval); |
| gboolean __g_atomic_pointer_compare_and_exchange (volatile gpointer G_GNUC_MAY_ALIAS *atomic, |
| gpointer oldval, |
| gpointer newval); |
| |
| gboolean |
| g_atomic_pointer_compare_and_exchange (volatile gpointer G_GNUC_MAY_ALIAS *atomic, |
| gpointer oldval, |
| gpointer newval) |
| { |
| if (G_UNLIKELY (CRIS_ATOMIC_BREAKS_CACHELINE (atomic))) |
| return __g_atomic_pointer_compare_and_exchange (atomic, oldval, newval); |
| |
| return CRIS_ATOMIC_INT_CMP_XCHG (atomic, oldval, newval); |
| } |
| |
| gboolean |
| g_atomic_int_compare_and_exchange (volatile gint G_GNUC_MAY_ALIAS *atomic, |
| gint oldval, |
| gint newval) |
| { |
| if (G_UNLIKELY (CRIS_ATOMIC_BREAKS_CACHELINE (atomic))) |
| return __g_atomic_int_compare_and_exchange (atomic, oldval, newval); |
| |
| return CRIS_ATOMIC_INT_CMP_XCHG (atomic, oldval, newval); |
| } |
| |
| gint |
| g_atomic_int_exchange_and_add (volatile gint G_GNUC_MAY_ALIAS *atomic, |
| gint val) |
| { |
| gint result; |
| |
| if (G_UNLIKELY (CRIS_ATOMIC_BREAKS_CACHELINE (atomic))) |
| return __g_atomic_int_exchange_and_add (atomic, val); |
| |
| do |
| result = *atomic; |
| while (!CRIS_ATOMIC_INT_CMP_XCHG (atomic, result, result + val)); |
| |
| return result; |
| } |
| |
| void |
| g_atomic_int_add (volatile gint G_GNUC_MAY_ALIAS *atomic, |
| gint val) |
| { |
| gint result; |
| |
| if (G_UNLIKELY (CRIS_ATOMIC_BREAKS_CACHELINE (atomic))) |
| return __g_atomic_int_add (atomic, val); |
| |
| do |
| result = *atomic; |
| while (!CRIS_ATOMIC_INT_CMP_XCHG (atomic, result, result + val)); |
| } |
| |
| /* We need the atomic mutex for atomic operations where the atomic variable |
| * breaks the 32 byte cache line since the CRIS architecture does not support |
| * atomic operations on such variables. Fortunately this should be rare. |
| */ |
| # define DEFINE_WITH_MUTEXES |
| # define g_atomic_int_exchange_and_add __g_atomic_int_exchange_and_add |
| # define g_atomic_int_add __g_atomic_int_add |
| # define g_atomic_int_compare_and_exchange __g_atomic_int_compare_and_exchange |
| # define g_atomic_pointer_compare_and_exchange __g_atomic_pointer_compare_and_exchange |
| |
| # else /* !G_ATOMIC_* */ |
| # define DEFINE_WITH_MUTEXES |
| # endif /* G_ATOMIC_* */ |
| #else /* !__GNUC__ */ |
| # ifdef G_PLATFORM_WIN32 |
| # define DEFINE_WITH_WIN32_INTERLOCKED |
| # else |
| # define DEFINE_WITH_MUTEXES |
| # endif |
| #endif /* __GNUC__ */ |
| |
| #ifdef DEFINE_WITH_WIN32_INTERLOCKED |
| # include <windows.h> |
| /* Following indicates that InterlockedCompareExchangePointer is |
| * declared in winbase.h (included by windows.h) and needs to be |
| * commented out if not true. It is defined iff WINVER > 0x0400, |
| * which is usually correct but can be wrong if WINVER is set before |
| * windows.h is included. |
| */ |
| # if WINVER > 0x0400 |
| # define HAVE_INTERLOCKED_COMPARE_EXCHANGE_POINTER |
| # endif |
| |
| gint32 |
| g_atomic_int_exchange_and_add (volatile gint32 G_GNUC_MAY_ALIAS *atomic, |
| gint32 val) |
| { |
| return InterlockedExchangeAdd (atomic, val); |
| } |
| |
| void |
| g_atomic_int_add (volatile gint32 G_GNUC_MAY_ALIAS *atomic, |
| gint32 val) |
| { |
| InterlockedExchangeAdd (atomic, val); |
| } |
| |
| gboolean |
| g_atomic_int_compare_and_exchange (volatile gint32 G_GNUC_MAY_ALIAS *atomic, |
| gint32 oldval, |
| gint32 newval) |
| { |
| #ifndef HAVE_INTERLOCKED_COMPARE_EXCHANGE_POINTER |
| return (guint32) InterlockedCompareExchange ((PVOID*)atomic, |
| (PVOID)newval, |
| (PVOID)oldval) == oldval; |
| #else |
| return InterlockedCompareExchange (atomic, |
| newval, |
| oldval) == oldval; |
| #endif |
| } |
| |
| gboolean |
| g_atomic_pointer_compare_and_exchange (volatile gpointer G_GNUC_MAY_ALIAS *atomic, |
| gpointer oldval, |
| gpointer newval) |
| { |
| # ifdef HAVE_INTERLOCKED_COMPARE_EXCHANGE_POINTER |
| return InterlockedCompareExchangePointer (atomic, newval, oldval) == oldval; |
| # else |
| # if GLIB_SIZEOF_VOID_P != 4 /* no 32-bit system */ |
| # error "InterlockedCompareExchangePointer needed" |
| # else |
| return InterlockedCompareExchange (atomic, newval, oldval) == oldval; |
| # endif |
| # endif |
| } |
| #endif /* DEFINE_WITH_WIN32_INTERLOCKED */ |
| |
| #ifdef DEFINE_WITH_MUTEXES |
| /* We have to use the slow, but safe locking method */ |
| static GMutex *g_atomic_mutex; |
| |
| /** |
| * g_atomic_int_exchange_and_add: |
| * @atomic: a pointer to an integer |
| * @val: the value to add to *@atomic |
| * |
| * Atomically adds @val to the integer pointed to by @atomic. |
| * It returns the value of *@atomic just before the addition |
| * took place. Also acts as a memory barrier. |
| * |
| * Returns: the value of *@atomic before the addition. |
| * |
| * Since: 2.4 |
| */ |
| gint |
| g_atomic_int_exchange_and_add (volatile gint G_GNUC_MAY_ALIAS *atomic, |
| gint val) |
| { |
| gint result; |
| |
| g_mutex_lock (g_atomic_mutex); |
| result = *atomic; |
| *atomic += val; |
| g_mutex_unlock (g_atomic_mutex); |
| |
| return result; |
| } |
| |
| /** |
| * g_atomic_int_add: |
| * @atomic: a pointer to an integer |
| * @val: the value to add to *@atomic |
| * |
| * Atomically adds @val to the integer pointed to by @atomic. |
| * Also acts as a memory barrier. |
| * |
| * Since: 2.4 |
| */ |
| void |
| g_atomic_int_add (volatile gint G_GNUC_MAY_ALIAS *atomic, |
| gint val) |
| { |
| g_mutex_lock (g_atomic_mutex); |
| *atomic += val; |
| g_mutex_unlock (g_atomic_mutex); |
| } |
| |
| /** |
| * g_atomic_int_compare_and_exchange: |
| * @atomic: a pointer to an integer |
| * @oldval: the assumed old value of *@atomic |
| * @newval: the new value of *@atomic |
| * |
| * Compares @oldval with the integer pointed to by @atomic and |
| * if they are equal, atomically exchanges *@atomic with @newval. |
| * Also acts as a memory barrier. |
| * |
| * Returns: %TRUE, if *@atomic was equal @oldval. %FALSE otherwise. |
| * |
| * Since: 2.4 |
| */ |
| gboolean |
| g_atomic_int_compare_and_exchange (volatile gint G_GNUC_MAY_ALIAS *atomic, |
| gint oldval, |
| gint newval) |
| { |
| gboolean result; |
| |
| g_mutex_lock (g_atomic_mutex); |
| if (*atomic == oldval) |
| { |
| result = TRUE; |
| *atomic = newval; |
| } |
| else |
| result = FALSE; |
| g_mutex_unlock (g_atomic_mutex); |
| |
| return result; |
| } |
| |
| /** |
| * g_atomic_pointer_compare_and_exchange: |
| * @atomic: a pointer to a #gpointer |
| * @oldval: the assumed old value of *@atomic |
| * @newval: the new value of *@atomic |
| * |
| * Compares @oldval with the pointer pointed to by @atomic and |
| * if they are equal, atomically exchanges *@atomic with @newval. |
| * Also acts as a memory barrier. |
| * |
| * Returns: %TRUE, if *@atomic was equal @oldval. %FALSE otherwise. |
| * |
| * Since: 2.4 |
| */ |
| gboolean |
| g_atomic_pointer_compare_and_exchange (volatile gpointer G_GNUC_MAY_ALIAS *atomic, |
| gpointer oldval, |
| gpointer newval) |
| { |
| gboolean result; |
| |
| g_mutex_lock (g_atomic_mutex); |
| if (*atomic == oldval) |
| { |
| result = TRUE; |
| *atomic = newval; |
| } |
| else |
| result = FALSE; |
| g_mutex_unlock (g_atomic_mutex); |
| |
| return result; |
| } |
| |
| #ifdef G_ATOMIC_OP_MEMORY_BARRIER_NEEDED |
| |
| /** |
| * g_atomic_int_get: |
| * @atomic: a pointer to an integer |
| * |
| * Reads the value of the integer pointed to by @atomic. |
| * Also acts as a memory barrier. |
| * |
| * Returns: the value of *@atomic |
| * |
| * Since: 2.4 |
| */ |
| gint |
| (g_atomic_int_get) (volatile gint G_GNUC_MAY_ALIAS *atomic) |
| { |
| gint result; |
| |
| g_mutex_lock (g_atomic_mutex); |
| result = *atomic; |
| g_mutex_unlock (g_atomic_mutex); |
| |
| return result; |
| } |
| |
| /** |
| * g_atomic_int_set: |
| * @atomic: a pointer to an integer |
| * @newval: the new value |
| * |
| * Sets the value of the integer pointed to by @atomic. |
| * Also acts as a memory barrier. |
| * |
| * Since: 2.10 |
| */ |
| void |
| (g_atomic_int_set) (volatile gint G_GNUC_MAY_ALIAS *atomic, |
| gint newval) |
| { |
| g_mutex_lock (g_atomic_mutex); |
| *atomic = newval; |
| g_mutex_unlock (g_atomic_mutex); |
| } |
| |
| /** |
| * g_atomic_pointer_get: |
| * @atomic: a pointer to a #gpointer. |
| * |
| * Reads the value of the pointer pointed to by @atomic. |
| * Also acts as a memory barrier. |
| * |
| * Returns: the value to add to *@atomic. |
| * |
| * Since: 2.4 |
| */ |
| gpointer |
| (g_atomic_pointer_get) (volatile gpointer G_GNUC_MAY_ALIAS *atomic) |
| { |
| gpointer result; |
| |
| g_mutex_lock (g_atomic_mutex); |
| result = *atomic; |
| g_mutex_unlock (g_atomic_mutex); |
| |
| return result; |
| } |
| |
| /** |
| * g_atomic_pointer_set: |
| * @atomic: a pointer to a #gpointer |
| * @newval: the new value |
| * |
| * Sets the value of the pointer pointed to by @atomic. |
| * Also acts as a memory barrier. |
| * |
| * Since: 2.10 |
| */ |
| void |
| (g_atomic_pointer_set) (volatile gpointer G_GNUC_MAY_ALIAS *atomic, |
| gpointer newval) |
| { |
| g_mutex_lock (g_atomic_mutex); |
| *atomic = newval; |
| g_mutex_unlock (g_atomic_mutex); |
| } |
| #endif /* G_ATOMIC_OP_MEMORY_BARRIER_NEEDED */ |
| #elif defined (G_ATOMIC_OP_MEMORY_BARRIER_NEEDED) |
| gint |
| (g_atomic_int_get) (volatile gint G_GNUC_MAY_ALIAS *atomic) |
| { |
| G_ATOMIC_MEMORY_BARRIER; |
| return *atomic; |
| } |
| |
| void |
| (g_atomic_int_set) (volatile gint G_GNUC_MAY_ALIAS *atomic, |
| gint newval) |
| { |
| *atomic = newval; |
| G_ATOMIC_MEMORY_BARRIER; |
| } |
| |
| gpointer |
| (g_atomic_pointer_get) (volatile gpointer G_GNUC_MAY_ALIAS *atomic) |
| { |
| G_ATOMIC_MEMORY_BARRIER; |
| return *atomic; |
| } |
| |
| void |
| (g_atomic_pointer_set) (volatile gpointer G_GNUC_MAY_ALIAS *atomic, |
| gpointer newval) |
| { |
| *atomic = newval; |
| G_ATOMIC_MEMORY_BARRIER; |
| } |
| #endif /* DEFINE_WITH_MUTEXES || G_ATOMIC_OP_MEMORY_BARRIER_NEEDED */ |
| |
| #ifdef ATOMIC_INT_CMP_XCHG |
| gboolean |
| g_atomic_int_compare_and_exchange (volatile gint G_GNUC_MAY_ALIAS *atomic, |
| gint oldval, |
| gint newval) |
| { |
| return ATOMIC_INT_CMP_XCHG (atomic, oldval, newval); |
| } |
| |
| gint |
| g_atomic_int_exchange_and_add (volatile gint G_GNUC_MAY_ALIAS *atomic, |
| gint val) |
| { |
| gint result; |
| do |
| result = *atomic; |
| while (!ATOMIC_INT_CMP_XCHG (atomic, result, result + val)); |
| |
| return result; |
| } |
| |
| void |
| g_atomic_int_add (volatile gint G_GNUC_MAY_ALIAS *atomic, |
| gint val) |
| { |
| gint result; |
| do |
| result = *atomic; |
| while (!ATOMIC_INT_CMP_XCHG (atomic, result, result + val)); |
| } |
| #endif /* ATOMIC_INT_CMP_XCHG */ |
| |
| void |
| _g_atomic_thread_init (void) |
| { |
| #ifdef DEFINE_WITH_MUTEXES |
| g_atomic_mutex = g_mutex_new (); |
| #endif /* DEFINE_WITH_MUTEXES */ |
| } |
| |
| #ifndef G_ATOMIC_OP_MEMORY_BARRIER_NEEDED |
| gint |
| (g_atomic_int_get) (volatile gint G_GNUC_MAY_ALIAS *atomic) |
| { |
| return g_atomic_int_get (atomic); |
| } |
| |
| void |
| (g_atomic_int_set) (volatile gint G_GNUC_MAY_ALIAS *atomic, |
| gint newval) |
| { |
| g_atomic_int_set (atomic, newval); |
| } |
| |
| gpointer |
| (g_atomic_pointer_get) (volatile gpointer G_GNUC_MAY_ALIAS *atomic) |
| { |
| return g_atomic_pointer_get (atomic); |
| } |
| |
| void |
| (g_atomic_pointer_set) (volatile gpointer G_GNUC_MAY_ALIAS *atomic, |
| gpointer newval) |
| { |
| g_atomic_pointer_set (atomic, newval); |
| } |
| #endif /* G_ATOMIC_OP_MEMORY_BARRIER_NEEDED */ |