New files to implement atomic operations for different platforms. Fixes

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.
This commit is contained in:
Sebastian Wilhelmi 2004-02-26 14:30:35 +00:00 committed by Sebastian Wilhelmi
parent fc9afe0d21
commit dbbb29f608
18 changed files with 1171 additions and 0 deletions

View 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

View 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

View 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

View 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

View 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

View 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

View 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"

View 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 ===

View 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">

View 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>

View File

@ -652,6 +652,25 @@ g_async_queue_timed_pop_unlocked
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>

View File

@ -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

View 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 \

178
glib/gatomic.c Normal file
View File

@ -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 */

545
glib/gatomic.h Normal file
View File

@ -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__ */

View 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>

View 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)

57
tests/atomic-test.c Normal file
View File

@ -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;
}