From: Sebastian Wilhelmi Date: Thu, 26 Feb 2004 14:30:35 +0000 (+0000) Subject: New files to implement atomic operations for different platforms. Fixes X-Git-Tag: GLIB_2_3_5~35 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=dbbb29f608fb5ffd31d1294b0d41e4a45b077e62;hp=fc9afe0d219ad7bb16c2ba8ce2a1e268bdd25479;p=platform%2Fupstream%2Fglib.git New files to implement atomic operations for different platforms. Fixes 2004-02-26 Sebastian Wilhelmi * glib/gatomic.c, glib/gatomic.h: New files to implement atomic operations for different platforms. Fixes bug #63621. * glib/glib.h: Include gatomic.h. * configure.in: Add test for assembler routines for atomic operations. * glib/Makefile.am: Add gatomic.c, gatomic.h. * tests/Makefile.am, tests/atomic-test.c: Unit test for atomic operations. * glib/glib-overrides.txt, glib/glib-sections.txt, glib/glib-docs.sgml, glib/tmpl/atomic_operations.sgml: Add docs for atomic operations. --- diff --git a/ChangeLog b/ChangeLog index 09c21db..a6852a3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,17 @@ +2004-02-26 Sebastian Wilhelmi + + * glib/gatomic.c, glib/gatomic.h: New files to implement atomic + operations for different platforms. Fixes bug #63621. + + * glib/glib.h: Include gatomic.h. + + * configure.in: Add test for assembler routines for atomic operations. + + * glib/Makefile.am: Add gatomic.c, gatomic.h. + + * tests/Makefile.am, tests/atomic-test.c: Unit test for atomic + operations. + 2003-02-26 Hans Breuer * glib/glib.def : added g_hash_table_find and a diff --git a/ChangeLog.pre-2-10 b/ChangeLog.pre-2-10 index 09c21db..a6852a3 100644 --- a/ChangeLog.pre-2-10 +++ b/ChangeLog.pre-2-10 @@ -1,3 +1,17 @@ +2004-02-26 Sebastian Wilhelmi + + * glib/gatomic.c, glib/gatomic.h: New files to implement atomic + operations for different platforms. Fixes bug #63621. + + * glib/glib.h: Include gatomic.h. + + * configure.in: Add test for assembler routines for atomic operations. + + * glib/Makefile.am: Add gatomic.c, gatomic.h. + + * tests/Makefile.am, tests/atomic-test.c: Unit test for atomic + operations. + 2003-02-26 Hans Breuer * glib/glib.def : added g_hash_table_find and a diff --git a/ChangeLog.pre-2-12 b/ChangeLog.pre-2-12 index 09c21db..a6852a3 100644 --- a/ChangeLog.pre-2-12 +++ b/ChangeLog.pre-2-12 @@ -1,3 +1,17 @@ +2004-02-26 Sebastian Wilhelmi + + * glib/gatomic.c, glib/gatomic.h: New files to implement atomic + operations for different platforms. Fixes bug #63621. + + * glib/glib.h: Include gatomic.h. + + * configure.in: Add test for assembler routines for atomic operations. + + * glib/Makefile.am: Add gatomic.c, gatomic.h. + + * tests/Makefile.am, tests/atomic-test.c: Unit test for atomic + operations. + 2003-02-26 Hans Breuer * glib/glib.def : added g_hash_table_find and a diff --git a/ChangeLog.pre-2-4 b/ChangeLog.pre-2-4 index 09c21db..a6852a3 100644 --- a/ChangeLog.pre-2-4 +++ b/ChangeLog.pre-2-4 @@ -1,3 +1,17 @@ +2004-02-26 Sebastian Wilhelmi + + * glib/gatomic.c, glib/gatomic.h: New files to implement atomic + operations for different platforms. Fixes bug #63621. + + * glib/glib.h: Include gatomic.h. + + * configure.in: Add test for assembler routines for atomic operations. + + * glib/Makefile.am: Add gatomic.c, gatomic.h. + + * tests/Makefile.am, tests/atomic-test.c: Unit test for atomic + operations. + 2003-02-26 Hans Breuer * glib/glib.def : added g_hash_table_find and a diff --git a/ChangeLog.pre-2-6 b/ChangeLog.pre-2-6 index 09c21db..a6852a3 100644 --- a/ChangeLog.pre-2-6 +++ b/ChangeLog.pre-2-6 @@ -1,3 +1,17 @@ +2004-02-26 Sebastian Wilhelmi + + * glib/gatomic.c, glib/gatomic.h: New files to implement atomic + operations for different platforms. Fixes bug #63621. + + * glib/glib.h: Include gatomic.h. + + * configure.in: Add test for assembler routines for atomic operations. + + * glib/Makefile.am: Add gatomic.c, gatomic.h. + + * tests/Makefile.am, tests/atomic-test.c: Unit test for atomic + operations. + 2003-02-26 Hans Breuer * glib/glib.def : added g_hash_table_find and a diff --git a/ChangeLog.pre-2-8 b/ChangeLog.pre-2-8 index 09c21db..a6852a3 100644 --- a/ChangeLog.pre-2-8 +++ b/ChangeLog.pre-2-8 @@ -1,3 +1,17 @@ +2004-02-26 Sebastian Wilhelmi + + * glib/gatomic.c, glib/gatomic.h: New files to implement atomic + operations for different platforms. Fixes bug #63621. + + * glib/glib.h: Include gatomic.h. + + * configure.in: Add test for assembler routines for atomic operations. + + * glib/Makefile.am: Add gatomic.c, gatomic.h. + + * tests/Makefile.am, tests/atomic-test.c: Unit test for atomic + operations. + 2003-02-26 Hans Breuer * glib/glib.def : added g_hash_table_find and a diff --git a/configure.in b/configure.in index fc16232..c47e704 100644 --- a/configure.in +++ b/configure.in @@ -1864,6 +1864,63 @@ if test $mutex_has_default = yes ; then LIBS="$glib_save_LIBS" fi +dnl ***************************** +dnl *** GAtomic tests for gcc *** +dnl ***************************** + +AC_MSG_CHECKING([whether to use inline assembler routines for atomic integers]) + +if test x"$GCC" = xyes; then + case $host_cpu in + i386) + AC_MSG_RESULT([no]) + ;; + i?86) + AC_MSG_RESULT([i486]) + glib_atomic_inlined_implementation=I486 + ;; + sparc*) + SPARCV9_WARNING="Try to rerun configure with CFLAGS='-mcpu=v9', + when you are using a sparc with v9 instruction set (most + sparcs nowadays). This will make the code for atomic + operations much faster. The resulting code will not run + on very old sparcs though." + + AC_LINK_IFELSE([[ + main () + { + int tmp1, tmp2, tmp3; + __asm__ __volatile__("casx [%2], %0, %1" + : "=&r" (tmp1), "=&r" (tmp2) : "r" (&tmp3)); + }]], + AC_MSG_RESULT([sparcv9]) + glib_atomic_inlined_implementation=SPARCV9, + AC_MSG_RESULT([no]) + AC_MSG_WARN([[$SPARCV9_WARNING]])) + ;; + alpha) + AC_MSG_RESULT([alpha]) + glib_atomic_inlined_implementation=ALPHA + ;; + x86_64) + AC_MSG_RESULT([x86_64]) + glib_atomic_inlined_implementation=X86_64 + ;; + powerpc*) + AC_MSG_RESULT([powerpc]) + glib_atomic_inlined_implementation=POWERPC + ;; + ia64) + AC_MSG_RESULT([ia64]) + glib_atomic_inlined_implementation=IA64 + ;; + *) + AC_MSG_RESULT([none]) + glib_atomic_inlined_implementation=NONE + ;; + esac +fi + dnl **************************************** dnl *** GLib POLL* compatibility defines *** dnl **************************************** @@ -2237,6 +2294,10 @@ union _GSystemThread long dummy_long; }; _______EOF + if test x"$g_atomic_inlined_implementation" != x; then + echo >>$outfile + echo "#define G_ATOMIC_INLINED_IMPLEMENTATION_$g_atomic_inlined_implementation" >>$outfile + fi echo >>$outfile g_bit_sizes="16 32 64" @@ -2540,6 +2601,8 @@ g_mutex_sizeof="$glib_cv_sizeof_gmutex" g_system_thread_sizeof="$glib_cv_sizeof_system_thread" g_mutex_contents="$glib_cv_byte_contents_gmutex" +g_atomic_inlined_implementation="$glib_atomic_inlined_implementation" + g_module_suffix="$glib_gmodule_suffix" g_pid_type="$glib_pid_type" diff --git a/docs/reference/ChangeLog b/docs/reference/ChangeLog index 546ec76..2583cac 100644 --- a/docs/reference/ChangeLog +++ b/docs/reference/ChangeLog @@ -1,3 +1,9 @@ +2004-02-26 Sebastian Wilhelmi + + * glib/glib-overrides.txt, glib/glib-sections.txt, + glib/glib-docs.sgml, glib/tmpl/atomic_operations.sgml: Add docs + for atomic operations. + Tue Feb 24 14:09:21 2004 Owen Taylor * === Released 2.3.3 === diff --git a/docs/reference/glib/glib-docs.sgml b/docs/reference/glib/glib-docs.sgml index 48c6d23..66ed2b2 100644 --- a/docs/reference/glib/glib-docs.sgml +++ b/docs/reference/glib/glib-docs.sgml @@ -8,6 +8,7 @@ + @@ -106,6 +107,7 @@ synchronize their operation. &glib-Byte-Order-Macros; &glib-Numerical-Definitions; &glib-Miscellaneous-Macros; + &glib-Atomic-Operations; diff --git a/docs/reference/glib/glib-overrides.txt b/docs/reference/glib/glib-overrides.txt index 0f38440..f489865 100644 --- a/docs/reference/glib/glib-overrides.txt +++ b/docs/reference/glib/glib-overrides.txt @@ -288,6 +288,62 @@ gchar c gchar c +# g_atomic + + +g_atomic_int_get +gint32 +gint32 *atomic + + + +g_atomic_int_exchange_and_add +gint32 +gint32 *atomic +gint32 val + + + +g_atomic_int_add +void +gint32 *atomic +gint32 val + + + +g_atomic_int_compare_and_exchange +gboolean +gint32 *atomic +gint32 oldval +gint32 newval + + + +g_atomic_pointer_get +gpointer +gpointer *atomic + + + +g_atomic_pointer_compare_and_exchange +gboolean +gpointer *atomic +gpointer oldval +gpointer newval + + + +g_atomic_int_inc +void +gint32 *atomic + + + +g_atomic_int_dec_and_test +gboolean +gint32 *atomic + + GIConv diff --git a/docs/reference/glib/glib-sections.txt b/docs/reference/glib/glib-sections.txt index 0b9365a..16ed31c 100644 --- a/docs/reference/glib/glib-sections.txt +++ b/docs/reference/glib/glib-sections.txt @@ -653,6 +653,25 @@ g_async_queue_length_unlocked
+Atomic Operations +atomic_operations +g_atomic_int_get +g_atomic_int_add +g_atomic_int_exchange_and_add +g_atomic_int_compare_and_exchange +g_atomic_pointer_get +g_atomic_pointer_compare_and_exchange +g_atomic_int_inc +g_atomic_int_dec_and_test +
+ + +g_atomic_int_add_fallback +g_atomic_int_exchange_and_add_fallback +g_atomic_int_compare_and_exchange_fallback +g_atomic_pointer_compare_and_exchange_fallback + +
IO Channels iochannels GIOChannel diff --git a/docs/reference/glib/tmpl/atomic_operations.sgml b/docs/reference/glib/tmpl/atomic_operations.sgml new file mode 100644 index 0000000..8b76b06 --- /dev/null +++ b/docs/reference/glib/tmpl/atomic_operations.sgml @@ -0,0 +1,156 @@ + +Atomic Operations + + +basic atomic integer and pointer operations + + + +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. + + + + +You must not directly read integers or pointers concurrently accessed +by other threads with with the following functions directly. Always use +g_atomic_int_get() and g_atomic_pointer_get() respectively. They are +acting as a memory barrier. + + + + + +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(). + + + + + +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. + + + + + + + + +#GMutex +GLib mutual exclusions. + + + + + + + +Reads the value of the integer pointed to by @atomic. Also acts as +a memory barrier. + + +@atomic: a pointer to a 32-bit integer. +@Returns: the value of *@atomic. +@Since: 2.4 + + + + +Atomically adds @val to the integer pointed to by @atomic. +Also acts as a memory barrier. + + +@atomic: a pointer to a 32-bit integer. +@val: the value to add to *@atomic. +@Since: 2.4 + + + + +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. + + +@atomic: a pointer to a 32-bit integer. +@val: the value to add to *@atomic. +@Returns: the value of *@atomic before the addition. +@Since: 2.4 + + + + +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. + + +@atomic: a pointer to a 32-bit integer. +@oldval: the assumed old value of *@atomic. +@newval: the new value of *@atomic. +@Returns: %TRUE, if *@atomic was equal @oldval. %FALSE otherwise. +@Since: 2.4 + + + + +Reads the value of the pointer pointed to by @atomic. Also acts as +a memory barrier. + + +@atomic: a pointer to a #gpointer. +@Returns: the value to add to *@atomic. +@Since: 2.4 + + + + +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. + + +@atomic: a pointer to a #gpointer. +@oldval: the assumed old value of *@atomic. +@newval: the new value of *@atomic. +@Returns: %TRUE, if *@atomic was equal @oldval. %FALSE otherwise. +@Since: 2.4 + + + + +Atomically increments the integer pointed to by @atomic by 1. + + +@atomic: a pointer to a 32-bit integer. +@Since: 2.4 + + + + +Atomically decrements the integer pointed to by @atomic by 1. + + +@atomic: a pointer to a 32-bit integer. +@Returns: %TRUE, if the integer pointed to by @atomic is 0 after +decrementing it. +@Since: 2.4 + + diff --git a/glib/Makefile.am b/glib/Makefile.am index 2741dcc..abd8113 100644 --- a/glib/Makefile.am +++ b/glib/Makefile.am @@ -44,6 +44,7 @@ endif libglib_2_0_la_SOURCES = \ garray.c \ gasyncqueue.c \ + gatomic.c \ gbacktrace.c \ gbsearcharray.h \ gcache.c \ @@ -112,6 +113,7 @@ glibsubinclude_HEADERS = \ galloca.h \ garray.h \ gasyncqueue.h \ + gatomic.h \ gbacktrace.h \ gcache.h \ gcompletion.h \ diff --git a/glib/gatomic.c b/glib/gatomic.c new file mode 100644 index 0000000..eaa7f61 --- /dev/null +++ b/glib/gatomic.c @@ -0,0 +1,178 @@ +/* GLIB - Library of useful routines for C programming + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * GAtomic: atomic integer operation. + * Copyright (C) 2003 Sebastian Wilhelmi + * + * 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 + +#ifdef G_THREADS_ENABLED +# if !defined (G_ATOMIC_USE_FALLBACK_IMPLEMENTATION) +/* We have an inline implementation, which we can now use for the + * fallback implementation. This fallback implementation is only used by + * modules, which are not compliled with gcc + */ + +gint32 +g_atomic_int_exchange_and_add_fallback (gint32 *atomic, + gint32 val) +{ + return g_atomic_int_exchange_and_add (atomic, val); +} + + +void +g_atomic_int_add_fallback (gint32 *atomic, + gint32 val) +{ + g_atomic_int_add (atomic, val); +} + +gboolean +g_atomic_int_compare_and_exchange_fallback (gint32 *atomic, + gint32 oldval, + gint32 newval) +{ + return g_atomic_int_compare_and_exchange (atomic, oldval, newval); +} + +gboolean +g_atomic_pointer_compare_and_exchange_fallback (gpointer *atomic, + gpointer oldval, + gpointer newval) +{ + return g_atomic_pointer_compare_and_exchange (atomic, oldval, newval); +} + +gint32 +g_atomic_int_get_fallback (gint32 *atomic) +{ + return g_atomic_int_get (atomic); +} + +gint32 +g_atomic_pointer_get_fallback (gpointer *atomic) +{ + return g_atomic_int_get (atomic); +} + +# else /* !G_ATOMIC_USE_FALLBACK_IMPLEMENTATION */ +/* We have to use the slow, but safe locking method */ +G_LOCK_DEFINE_STATIC (g_atomic_lock); + +gint32 +g_atomic_int_exchange_and_add_fallback (gint32 *atomic, + gint32 val) +{ + gint32 result; + + G_LOCK (g_atomic_lock); + result = *atomic; + *atomic += val; + G_UNLOCK (g_atomic_lock); + + return result; +} + + +void +g_atomic_int_add_fallback (gint32 *atomic, + gint32 val) +{ + G_LOCK (g_atomic_lock); + *atomic += val; + G_UNLOCK (g_atomic_lock); +} + +gboolean +g_atomic_int_compare_and_exchange_fallback (gint32 *atomic, + gint32 oldval, + gint32 newval) +{ + gboolean result; + + G_LOCK (g_atomic_lock); + if (*atomic == oldval) + { + result = TRUE; + *atomic = newval; + } + else + result = FALSE; + G_UNLOCK (g_atomic_lock); + + return result; +} + +gboolean +g_atomic_pointer_compare_and_exchange_fallback (gpointer *atomic, + gpointer oldval, + gpointer newval) +{ + gboolean result; + + G_LOCK (g_atomic_lock); + if (*atomic == oldval) + { + result = TRUE; + *atomic = newval; + } + else + result = FALSE; + G_UNLOCK (g_atomic_lock); + + return result; +} + +static inline gint32 +g_atomic_int_get_fallback (gint32 *atomic) +{ + gint32 result; + + G_LOCK (g_atomic_lock); + result = *atomic; + G_UNLOCK (g_atomic_lock); + + return result; +} + +static inline gpointer +g_atomic_pointer_get_fallback (gpointer *atomic) +{ + gpointer result; + + G_LOCK (g_atomic_lock); + result = *atomic; + G_UNLOCK (g_atomic_lock); + + return result; +} + + +# endif /* G_ATOMIC_USE_FALLBACK_IMPLEMENTATION */ +#else /* !G_THREADS_ENABLED */ +gint32 g_atomic_int_exchange_and_add (gint32 *atomic, + gint32 val) +{ + gint32 result = *atomic; + *atomic += val; + return result; +} +#endif /* G_THREADS_ENABLED */ + diff --git a/glib/gatomic.h b/glib/gatomic.h new file mode 100644 index 0000000..1ce92bb --- /dev/null +++ b/glib/gatomic.h @@ -0,0 +1,545 @@ +/* GLIB - Library of useful routines for C programming + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * GAtomic: atomic integer operation. + * Copyright (C) 2003 Sebastian Wilhelmi + * + * 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. + */ + +/* + * Modified by the GLib Team and others 1997-2000. See the AUTHORS + * file for a list of people on the GLib Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GLib at ftp://ftp.gtk.org/pub/gtk/. + */ + +#ifndef __G_ATOMIC_H__ +#define __G_ATOMIC_H__ + +#include + +G_BEGIN_DECLS + +#ifdef G_THREADS_ENABLED + +gint32 g_atomic_int_exchange_and_add_fallback (gint32 *atomic, + gint32 val); +void g_atomic_int_add_fallback (gint32 *atomic, + gint32 val); +gboolean g_atomic_int_compare_and_exchange_fallback (gint32 *atomic, + gint32 oldval, + gint32 newval); +gboolean g_atomic_pointer_compare_and_exchange_fallback (gpointer *atomic, + gpointer oldval, + gpointer newval); + +# if defined (__GNUC__) +# if defined (G_ATOMIC_INLINED_IMPLEMENTATION_I486) +/* Adapted from CVS version 1.10 of glibc's sysdeps/i386/i486/bits/atomic.h + */ +static inline gint32 +g_atomic_int_exchange_and_add (gint32 *atomic, + gint32 val) +{ + gint32 result; + + __asm__ __volatile__ ("lock; xaddl %0,%1" + : "=r" (result), "=m" (*atomic) + : "0" (val), "m" (*atomic)); + return result; +} + +static inline void +g_atomic_int_add (gint32 *atomic, + gint32 val) +{ + __asm__ __volatile__ ("lock; addl %1,%0" + : "=m" (*atomic) + : "ir" (val), "m" (*atomic)); +} + +static inline gboolean +g_atomic_int_compare_and_exchange (gint32 *atomic, + gint32 oldval, + gint32 newval) +{ + gint32 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 */ + +static inline gboolean +g_atomic_pointer_compare_and_exchange (gpointer *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; +} + +# define G_ATOMIC_MEMORY_BARRIER() /* Not needed */ + +# elif defined(G_ATOMIC_INLINED_IMPLEMENTATION_SPARCV9) \ + && (defined(__sparcv8) || defined(__sparcv9) || defined(__sparc_v9__)) +/* Adapted from CVS version 1.3 of glibc's sysdeps/sparc/sparc64/bits/atomic.h + */ +/* Why the test for __sparcv8, wheras really the sparcv9 architecture + * is required for the folowing assembler instructions? On + * sparc-solaris the only difference detectable at compile time + * between no -m and -mcpu=v9 is __sparcv8. + * + * However, in case -mcpu=v8 is set, the assembler will fail. This + * should be rare however, as there are only very few v8-not-v9 + * machines still out there (and we can't do better). + */ +static inline gboolean +g_atomic_int_compare_and_exchange (gint32 *atomic, + gint32 oldval, + gint32 newval) +{ + gint32 result; + __asm __volatile ("cas [%4], %2, %0" + : "=r" (result), "=m" (*atomic) + : "r" (oldval), "m" (*atomic), "r" (atomic), + "0" (newval)); + return result != 0; +} + +# if GLIB_SIZEOF_VOID_P == 4 /* 32-bit system */ +static inline gboolean +g_atomic_pointer_compare_and_exchange (gpointer *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 != 0; +} +# elif GLIB_SIZEOF_VOID_P == 8 /* 64-bit system */ +static inline gboolean +g_atomic_pointer_compare_and_exchange (gpointer *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 != 0; +} +# else /* What's that */ +# error "Your system has an unsupported pointer size" +# endif /* GLIB_SIZEOF_VOID_P */ +static inline gint32 +g_atomic_int_exchange_and_add (gint32 *atomic, + gint32 val) +{ + gint32 result; + do + result = *atomic; + while (!g_atomic_int_compare_and_exchange (atomic, result, result + val)); + + return result; +} + +static inline void +g_atomic_int_add (gint32 *atomic, + gint32 val) +{ + g_atomic_int_exchange_and_add (atomic, val); +} + +# define G_ATOMIC_MEMORY_BARRIER() \ + __asm __volatile ("membar #LoadLoad | #LoadStore" \ + " | #StoreLoad | #StoreStore" : : : "memory") + +# elif defined(G_ATOMIC_INLINED_IMPLEMENTATION_ALPHA) +/* Adapted from CVS version 1.3 of glibc's sysdeps/alpha/bits/atomic.h + */ +static inline gboolean +g_atomic_int_compare_and_exchange (gint32 *atomic, + gint32 oldval, + gint32 newval) +{ + gint32 result; + gint32 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" ((gint64)oldval), + "Ir" (newval) + : "memory"); + return result != 0; +} +# if GLIB_SIZEOF_VOID_P == 4 /* 32-bit system */ +static inline gboolean +g_atomic_pointer_compare_and_exchange (gpointer *atomic, + gpointer oldval, + gpointer newval) +{ + gint32 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" ((gint64)oldval), + "Ir" (newval) + : "memory"); + return result != 0; +} +# elif GLIB_SIZEOF_VOID_P == 8 /* 64-bit system */ +static inline gboolean +g_atomic_pointer_compare_and_exchange (gpointer *atomic, + gpointer oldval, + gpointer newval) +{ + gint32 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" ((gint64)oldval), + "Ir" (newval) + : "memory"); + return result != 0; +} +# else /* What's that */ +# error "Your system has an unsupported pointer size" +# endif /* GLIB_SIZEOF_VOID_P */ +static inline gint32 +g_atomic_int_exchange_and_add (gint32 *atomic, + gint32 val) +{ + gint32 result; + do + result = *atomic; + while (!g_atomic_int_compare_and_exchange (atomic, result, result + val)); + + return result; +} + +static inline void +g_atomic_int_add (gint32 *atomic, + gint32 val) +{ + g_atomic_int_exchange_and_add (atomic, val); +} + +# define G_ATOMIC_MEMORY_BARRIER() __asm ("mb" : : : "memory") + +# elif defined(G_ATOMIC_INLINED_IMPLEMENTATION_X86_64) +/* Adapted from CVS version 1.9 of glibc's sysdeps/x86_64/bits/atomic.h + */ +static inline gint32 +g_atomic_int_exchange_and_add (gint32 *atomic, + gint32 val) +{ + gint32 result; + + __asm__ __volatile__ ("lock; xaddl %0,%1" + : "=r" (result), "=m" (*atomic) + : "0" (val), "m" (*atomic)); + return result; +} + +static inline void +g_atomic_int_add (gint32 *atomic, + gint32 val) +{ + __asm__ __volatile__ ("lock; addl %1,%0" + : "=m" (*atomic) + : "ir" (val), "m" (*atomic)); +} + +static inline gboolean +g_atomic_int_compare_and_exchange (gint32 *atomic, + gint32 oldval, + gint32 newval) +{ + gint32 result; + + __asm __volatile ("lock; cmpxchgl %2, %1" + : "=a" (result), "=m" (*atomic) + : "r" (newval), "m" (*atomic), "0" (oldval)); + + return result == oldval; +} + +static inline gboolean +g_atomic_pointer_compare_and_exchange (gpointer *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; +} + +# define G_ATOMIC_MEMORY_BARRIER() /* Not needed */ + +# elif defined(G_ATOMIC_INLINED_IMPLEMENTATION_POWERPC) +/* Adapted from CVS version 1.12 of glibc's sysdeps/powerpc/bits/atomic.h + * and CVS version 1.3 of glibc's sysdeps/powerpc/powerpc32/bits/atomic.h + * and CVS version 1.2 of glibc's sysdeps/powerpc/powerpc64/bits/atomic.h + */ +static inline gint32 +g_atomic_int_exchange_and_add (gint32 *atomic, + gint32 val) +{ + gint32 result, temp; + __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), "2" (*atomic) + : "cr0", "memory"); + return result; +} + +static inline void +g_atomic_int_add (gint32 *atomic, + gint32 val) +{ + g_atomic_int_exchange_and_add (atomic, val); +} + +# if GLIB_SIZEOF_VOID_P == 4 /* 32-bit system */ +static inline gboolean +g_atomic_int_compare_and_exchange (gint32 *atomic, + gint32 oldval, + gint32 newval) +{ + gint32 result; + __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"); + return result == 0; +} + +static inline gboolean +g_atomic_pointer_compare_and_exchange (gpointer *atomic, + gpointer oldval, + gpointer newval) +{ + gpointer result; + __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"); + return result == 0; +} +# elif GLIB_SIZEOF_VOID_P == 8 /* 64-bit system */ +static inline gboolean +g_atomic_int_compare_and_exchange (gint32 *atomic, + gint32 oldval, + gint32 newval) +{ + __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"); + return result == 0; +} + +static inline gboolean +g_atomic_pointer_compare_and_exchange (gpointer *atomic, + gpointer oldval, + gpointer newval) +{ + gpointer result; + __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"); + 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_INLINED_IMPLEMENTATION_IA64) +/* Adapted from CVS version 1.8 of glibc's sysdeps/ia64/bits/atomic.h + */ +static inline gint32 +g_atomic_int_exchange_and_add (gint32 *atomic, + gint32 val) +{ + return __sync_fetch_and_add_si (atomic, val); +} + +static inline void +g_atomic_int_add (gint32 *atomic, + gint32 val) +{ + __sync_fetch_and_add_si (atomic, val); +} + +static inline gboolean +g_atomic_int_compare_and_exchange (gint32 *atomic, + gint32 oldval, + gint32 newval) +{ + return __sync_bool_compare_and_exchange_si (atomic, oldval, newval); +} + +static inline gboolean +g_atomic_pointer_compare_and_exchange (gpointer *atomic, + gpointer oldval, + gpointer newval) +{ + return __sync_bool_compare_and_exchange_di ((long *)atomic, + (long)oldval, (long)newval); +} + +# define G_ATOMIC_MEMORY_BARRIER() __sync_synchronize () + +# else /* !G_ATOMIC_INLINED_IMPLEMENTATION_... */ +# define G_ATOMIC_USE_FALLBACK_IMPLEMENTATION +# endif /* G_ATOMIC_INLINED_IMPLEMENTATION_... */ +# else /* !__GNU__ */ +# define G_ATOMIC_USE_FALLBACK_IMPLEMENTATION +# endif /* __GNUC__ */ +#else /* !G_THREADS_ENABLED */ +gint32 g_atomic_int_exchange_and_add (gint32 *atomic, gint32 val); +# define g_atomic_int_add(atomic, val) (void)(*(atomic) += (val)) +# define g_atomic_int_compare_and_exchange(atomic, oldval, newval) \ + (*(atomic) == (oldval) ? (*(atomic) = (newval), TRUE) : FALSE) +# define g_atomic_pointer_compare_and_exchange(atomic, oldval, newval) \ + (*(atomic) == (oldval) ? (*(atomic) = (newval), TRUE) : FALSE) +# define g_atomic_int_get(atomic) (*(atomic)) +# define g_atomic_pointer_get(atomic) (*(atomic)) +#endif /* G_THREADS_ENABLED */ + +#ifdef G_ATOMIC_USE_FALLBACK_IMPLEMENTATION +# define g_atomic_int_exchange_and_add \ + g_atomic_int_exchange_and_add_fallback +# define g_atomic_int_add \ + g_atomic_int_add_fallback +# define g_atomic_int_compare_and_exchange \ + g_atomic_int_compare_and_exchange_fallback +# define g_atomic_pointer_compare_and_exchange \ + g_atomic_pointer_compare_and_exchange_fallback +# define g_atomic_int_get \ + g_atomic_int_get_fallback +# define g_atomic_pointer_get \ + g_atomic_pointer_get_fallback +#else /* !G_ATOMIC_USE_FALLBACK_IMPLEMENTATION */ +static inline gint32 +g_atomic_int_get (gint32 *atomic) +{ + gint32 result = *atomic; + G_ATOMIC_MEMORY_BARRIER (); + return result; +} + +static inline gpointer +g_atomic_pointer_get (gpointer *atomic) +{ + gpointer result = *atomic; + G_ATOMIC_MEMORY_BARRIER (); + return result; +} +#endif /* G_ATOMIC_USE_FALLBACK_IMPLEMENTATION */ + +#define g_atomic_int_inc(atomic) (g_atomic_int_add ((atomic), 1)) +#define g_atomic_int_dec_and_test(atomic) \ + (g_atomic_int_exchange_and_add ((atomic), -1) == 1) + +G_END_DECLS + +#endif /* __G_ATOMIC_H__ */ diff --git a/glib/glib.h b/glib/glib.h index f91d87f..b8a720e 100644 --- a/glib/glib.h +++ b/glib/glib.h @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include diff --git a/tests/Makefile.am b/tests/Makefile.am index 69c4764..b9f90ad 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -64,6 +64,7 @@ timeloop_closure_LDADD = $(libglib) $(libgobject) endif test_programs = \ + atomic-test \ array-test \ $(CXX_TEST) \ child-test \ @@ -115,6 +116,7 @@ progs_ldadd = $(EFENCE) $(libglib) $(EFENCE) thread_ldadd = $(libgthread) $(G_THREAD_LIBS) $(progs_ldadd) module_ldadd = $(libgmodule) $(G_MODULE_LIBS) $(progs_ldadd) +atomic_test_LDADD = $(progs_ldadd) array_test_LDADD = $(progs_ldadd) child_test_LDADD = $(thread_ldadd) completion_test_LDADD = $(progs_ldadd) diff --git a/tests/atomic-test.c b/tests/atomic-test.c new file mode 100644 index 0000000..0f94788 --- /dev/null +++ b/tests/atomic-test.c @@ -0,0 +1,57 @@ +#undef G_DISABLE_ASSERT +#undef G_LOG_DOMAIN + +#include + +/* Obviously we can't test that the operations are atomic, but we can + * at least test, that they do, what they ought to do */ + +int +main (int argc, + char *argv[]) +{ + gint i; + gint32 atomic = -5; + gpointer atomic_pointer = NULL; + gpointer biggest_pointer = atomic_pointer - 1; + + for (i = 0; i < 15; i++) + g_atomic_int_inc (&atomic); + g_assert (atomic == 10); + for (i = 0; i < 9; i++) + g_assert (!g_atomic_int_dec_and_test (&atomic)); + g_assert (g_atomic_int_dec_and_test (&atomic)); + g_assert (atomic == 0); + + g_assert (g_atomic_int_exchange_and_add (&atomic, 5) == 0); + g_assert (atomic == 5); + + g_assert (g_atomic_int_exchange_and_add (&atomic, -10) == 5); + g_assert (atomic == -5); + + g_atomic_int_add (&atomic, 20); + g_assert (atomic == 15); + + g_atomic_int_add (&atomic, -35); + g_assert (atomic == -20); + + g_assert (atomic == g_atomic_int_get (&atomic)); + + g_assert (g_atomic_int_compare_and_exchange (&atomic, -20, 20)); + g_assert (atomic == 20); + + g_assert (!g_atomic_int_compare_and_exchange (&atomic, 42, 12)); + g_assert (atomic == 20); + + g_assert (g_atomic_pointer_compare_and_exchange (&atomic_pointer, + NULL, biggest_pointer)); + g_assert (atomic_pointer == biggest_pointer); + + g_assert (atomic_pointer == g_atomic_pointer_get (&atomic_pointer)); + + g_assert (g_atomic_pointer_compare_and_exchange (&atomic_pointer, + biggest_pointer, NULL)); + g_assert (atomic_pointer == NULL); + + return 0; +}