summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog14
-rw-r--r--ChangeLog.pre-2-1014
-rw-r--r--ChangeLog.pre-2-1214
-rw-r--r--ChangeLog.pre-2-414
-rw-r--r--ChangeLog.pre-2-614
-rw-r--r--ChangeLog.pre-2-814
-rw-r--r--configure.in63
-rw-r--r--docs/reference/ChangeLog6
-rw-r--r--docs/reference/glib/glib-docs.sgml2
-rw-r--r--docs/reference/glib/glib-overrides.txt56
-rw-r--r--docs/reference/glib/glib-sections.txt19
-rw-r--r--docs/reference/glib/tmpl/atomic_operations.sgml156
-rw-r--r--glib/Makefile.am2
-rw-r--r--glib/gatomic.c178
-rw-r--r--glib/gatomic.h545
-rw-r--r--glib/glib.h1
-rw-r--r--tests/Makefile.am2
-rw-r--r--tests/atomic-test.c57
18 files changed, 1171 insertions, 0 deletions
diff --git a/ChangeLog b/ChangeLog
index 09c21db53..a6852a369 100644
--- 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
diff --git a/ChangeLog.pre-2-10 b/ChangeLog.pre-2-10
index 09c21db53..a6852a369 100644
--- a/ChangeLog.pre-2-10
+++ b/ChangeLog.pre-2-10
@@ -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
diff --git a/ChangeLog.pre-2-12 b/ChangeLog.pre-2-12
index 09c21db53..a6852a369 100644
--- a/ChangeLog.pre-2-12
+++ b/ChangeLog.pre-2-12
@@ -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
diff --git a/ChangeLog.pre-2-4 b/ChangeLog.pre-2-4
index 09c21db53..a6852a369 100644
--- a/ChangeLog.pre-2-4
+++ b/ChangeLog.pre-2-4
@@ -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
diff --git a/ChangeLog.pre-2-6 b/ChangeLog.pre-2-6
index 09c21db53..a6852a369 100644
--- a/ChangeLog.pre-2-6
+++ b/ChangeLog.pre-2-6
@@ -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
diff --git a/ChangeLog.pre-2-8 b/ChangeLog.pre-2-8
index 09c21db53..a6852a369 100644
--- a/ChangeLog.pre-2-8
+++ b/ChangeLog.pre-2-8
@@ -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
diff --git a/configure.in b/configure.in
index fc16232d1..c47e70406 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 546ec76c8..2583cacf0 100644
--- a/docs/reference/ChangeLog
+++ b/docs/reference/ChangeLog
@@ -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 ===
diff --git a/docs/reference/glib/glib-docs.sgml b/docs/reference/glib/glib-docs.sgml
index 48c6d230f..66ed2b2e6 100644
--- a/docs/reference/glib/glib-docs.sgml
+++ b/docs/reference/glib/glib-docs.sgml
@@ -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">
diff --git a/docs/reference/glib/glib-overrides.txt b/docs/reference/glib/glib-overrides.txt
index 0f38440dd..f4898652e 100644
--- a/docs/reference/glib/glib-overrides.txt
+++ b/docs/reference/glib/glib-overrides.txt
@@ -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>
diff --git a/docs/reference/glib/glib-sections.txt b/docs/reference/glib/glib-sections.txt
index 0b9365a51..16ed31c53 100644
--- a/docs/reference/glib/glib-sections.txt
+++ b/docs/reference/glib/glib-sections.txt
@@ -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
index 000000000..8b76b06f7
--- /dev/null
+++ b/docs/reference/glib/tmpl/atomic_operations.sgml
@@ -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
+
+
diff --git a/glib/Makefile.am b/glib/Makefile.am
index 2741dccb5..abd8113ee 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 000000000..eaa7f61bc
--- /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 <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
index 000000000..1ce92bb9d
--- /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 <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__ */
diff --git a/glib/glib.h b/glib/glib.h
index f91d87ff0..b8a720ee3 100644
--- a/glib/glib.h
+++ b/glib/glib.h
@@ -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>
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 69c4764d3..b9f90ad58 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 000000000..0f94788d0
--- /dev/null
+++ b/tests/atomic-test.c
@@ -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;
+}