New files to implement atomic operations for different platforms. Fixes
authorSebastian Wilhelmi <seppi@seppi.de>
Thu, 26 Feb 2004 14:30:35 +0000 (14:30 +0000)
committerSebastian Wilhelmi <wilhelmi@src.gnome.org>
Thu, 26 Feb 2004 14:30:35 +0000 (14:30 +0000)
2004-02-26  Sebastian Wilhelmi  <seppi@seppi.de>

* 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.

18 files changed:
ChangeLog
ChangeLog.pre-2-10
ChangeLog.pre-2-12
ChangeLog.pre-2-4
ChangeLog.pre-2-6
ChangeLog.pre-2-8
configure.in
docs/reference/ChangeLog
docs/reference/glib/glib-docs.sgml
docs/reference/glib/glib-overrides.txt
docs/reference/glib/glib-sections.txt
docs/reference/glib/tmpl/atomic_operations.sgml [new file with mode: 0644]
glib/Makefile.am
glib/gatomic.c [new file with mode: 0644]
glib/gatomic.h [new file with mode: 0644]
glib/glib.h
tests/Makefile.am
tests/atomic-test.c [new file with mode: 0644]

index 09c21db..a6852a3 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,17 @@
+2004-02-26  Sebastian Wilhelmi  <seppi@seppi.de>
+
+       * 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  <hans@breuer.org>
 
        * glib/glib.def : added g_hash_table_find and a
index 09c21db..a6852a3 100644 (file)
@@ -1,3 +1,17 @@
+2004-02-26  Sebastian Wilhelmi  <seppi@seppi.de>
+
+       * 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  <hans@breuer.org>
 
        * glib/glib.def : added g_hash_table_find and a
index 09c21db..a6852a3 100644 (file)
@@ -1,3 +1,17 @@
+2004-02-26  Sebastian Wilhelmi  <seppi@seppi.de>
+
+       * 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  <hans@breuer.org>
 
        * glib/glib.def : added g_hash_table_find and a
index 09c21db..a6852a3 100644 (file)
@@ -1,3 +1,17 @@
+2004-02-26  Sebastian Wilhelmi  <seppi@seppi.de>
+
+       * 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  <hans@breuer.org>
 
        * glib/glib.def : added g_hash_table_find and a
index 09c21db..a6852a3 100644 (file)
@@ -1,3 +1,17 @@
+2004-02-26  Sebastian Wilhelmi  <seppi@seppi.de>
+
+       * 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  <hans@breuer.org>
 
        * glib/glib.def : added g_hash_table_find and a
index 09c21db..a6852a3 100644 (file)
@@ -1,3 +1,17 @@
+2004-02-26  Sebastian Wilhelmi  <seppi@seppi.de>
+
+       * 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  <hans@breuer.org>
 
        * glib/glib.def : added g_hash_table_find and a
index fc16232..c47e704 100644 (file)
@@ -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"
index 546ec76..2583cac 100644 (file)
@@ -1,3 +1,9 @@
+2004-02-26  Sebastian Wilhelmi  <seppi@seppi.de>
+
+       * 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  <otaylor@redhat.com>
 
        * === Released 2.3.3 ===
index 48c6d23..66ed2b2 100644 (file)
@@ -8,6 +8,7 @@
 <!ENTITY glib-Byte-Order-Macros SYSTEM "xml/byte_order.xml">
 <!ENTITY glib-Numerical-Definitions SYSTEM "xml/numerical.xml">
 <!ENTITY glib-Miscellaneous-Macros SYSTEM "xml/macros_misc.xml">
+<!ENTITY glib-Atomic-Operations SYSTEM "xml/atomic_operations.xml">
 <!ENTITY glib-Memory-Allocation SYSTEM "xml/memory.xml">
 <!ENTITY glib-Error-Reporting SYSTEM "xml/error_reporting.xml">
 <!ENTITY glib-Warnings-and-Assertions SYSTEM "xml/warnings.xml">
@@ -106,6 +107,7 @@ synchronize their operation.
     &glib-Byte-Order-Macros;
     &glib-Numerical-Definitions;
     &glib-Miscellaneous-Macros;
+    &glib-Atomic-Operations;
   </chapter>
 
   <chapter id="glib-core">
index 0f38440..f489865 100644 (file)
@@ -288,6 +288,62 @@ gchar c
 gchar c
 </FUNCTION>
 
+# g_atomic
+
+<FUNCTION>
+<NAME>g_atomic_int_get</NAME>
+<RETURNS>gint32</RETURNS>
+gint32 *atomic
+</FUNCTION>
+
+<FUNCTION>
+<NAME>g_atomic_int_exchange_and_add</NAME>
+<RETURNS>gint32</RETURNS>
+gint32 *atomic
+gint32 val
+</FUNCTION>
+<FUNCTION>
+<NAME>g_atomic_int_add</NAME>
+<RETURNS>void</RETURNS>
+gint32 *atomic
+gint32 val
+</FUNCTION>
+<FUNCTION>
+<NAME>g_atomic_int_compare_and_exchange</NAME>
+<RETURNS>gboolean</RETURNS>
+gint32 *atomic
+gint32 oldval
+gint32 newval
+</FUNCTION>
+<FUNCTION>
+<NAME>g_atomic_pointer_get</NAME>
+<RETURNS>gpointer</RETURNS>
+gpointer *atomic
+</FUNCTION>
+
+<FUNCTION>
+<NAME>g_atomic_pointer_compare_and_exchange</NAME>
+<RETURNS>gboolean</RETURNS>
+gpointer *atomic
+gpointer oldval
+gpointer newval
+</FUNCTION>
+
+<FUNCTION>
+<NAME>g_atomic_int_inc</NAME>
+<RETURNS>void</RETURNS>
+gint32 *atomic
+</FUNCTION>
+
+<FUNCTION>
+<NAME>g_atomic_int_dec_and_test</NAME>
+<RETURNS>gboolean</RETURNS>
+gint32 *atomic
+</FUNCTION>
+
 <STRUCT>
 <NAME>GIConv</NAME>
 </STRUCT>
index 0b9365a..16ed31c 100644 (file)
@@ -653,6 +653,25 @@ g_async_queue_length_unlocked
 </SECTION>
 
 <SECTION>
+<TITLE>Atomic Operations</TITLE>
+<FILE>atomic_operations</FILE>
+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
+</SECTION>
+
+<SUBSECTION Private>
+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
+
+<SECTION>
 <TITLE>IO Channels</TITLE>
 <FILE>iochannels</FILE>
 GIOChannel
diff --git a/docs/reference/glib/tmpl/atomic_operations.sgml b/docs/reference/glib/tmpl/atomic_operations.sgml
new file mode 100644 (file)
index 0000000..8b76b06
--- /dev/null
@@ -0,0 +1,156 @@
+<!-- ##### SECTION Title ##### -->
+Atomic Operations
+
+<!-- ##### SECTION Short_Description ##### -->
+basic atomic integer and pointer operations
+
+<!-- ##### SECTION Long_Description ##### -->
+<para>
+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.  
+</para>
+
+<para>
+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.
+</para>
+
+<note>
+<para>
+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.
+</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>
+
+<!-- ##### SECTION See_Also ##### -->
+<para>
+<variablelist>
+<varlistentry>
+<term>#GMutex</term>
+<listitem><para>GLib mutual exclusions.</para></listitem>
+</varlistentry>
+</variablelist>
+</para>
+
+<!-- ##### FUNCTION g_atomic_int_get ##### -->
+<para>
+Reads the value of the integer pointed to by @atomic. Also acts as
+a memory barrier.
+</para>
+
+@atomic: a pointer to a 32-bit integer.
+@Returns: the value of *@atomic.
+@Since: 2.4
+
+
+<!-- ##### FUNCTION g_atomic_int_add ##### -->
+<para>
+Atomically adds @val to the integer pointed to by @atomic.
+Also acts as a memory barrier.
+</para>
+
+@atomic: a pointer to a 32-bit integer.
+@val: the value to add to *@atomic.
+@Since: 2.4
+
+
+<!-- ##### FUNCTION g_atomic_int_exchange_and_add ##### -->
+<para>
+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.
+</para>
+
+@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
+
+
+<!-- ##### FUNCTION g_atomic_int_compare_and_exchange ##### -->
+<para>
+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.
+</para>
+
+@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
+
+
+<!-- ##### FUNCTION g_atomic_pointer_get ##### -->
+<para>
+Reads the value of the pointer pointed to by @atomic. Also acts as
+a memory barrier.
+</para>
+
+@atomic: a pointer to a #gpointer.
+@Returns: the value to add to *@atomic.
+@Since: 2.4
+
+
+<!-- ##### FUNCTION g_atomic_pointer_compare_and_exchange ##### -->
+<para>
+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.
+</para>
+
+@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
+
+
+<!-- ##### FUNCTION g_atomic_int_inc ##### -->
+<para>
+Atomically increments the integer pointed to by @atomic by 1.
+</para>
+
+@atomic: a pointer to a 32-bit integer.
+@Since: 2.4
+
+
+<!-- ##### FUNCTION g_atomic_int_dec_and_test ##### -->
+<para>
+Atomically decrements the integer pointed to by @atomic by 1.
+</para>
+
+@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
+
+
index 2741dcc..abd8113 100644 (file)
@@ -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 (file)
index 0000000..eaa7f61
--- /dev/null
@@ -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 <glib.h>
+
+#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 (file)
index 0000000..1ce92bb
--- /dev/null
@@ -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 <glib/gtypes.h>
+
+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__ */
index f91d87f..b8a720e 100644 (file)
@@ -30,6 +30,7 @@
 #include <glib/galloca.h>
 #include <glib/garray.h>
 #include <glib/gasyncqueue.h>
+#include <glib/gatomic.h>
 #include <glib/gbacktrace.h>
 #include <glib/gcache.h>
 #include <glib/gcompletion.h>
index 69c4764..b9f90ad 100644 (file)
@@ -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 (file)
index 0000000..0f94788
--- /dev/null
@@ -0,0 +1,57 @@
+#undef G_DISABLE_ASSERT
+#undef G_LOG_DOMAIN
+
+#include <glib.h>
+
+/* 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;
+}