gatomic: introduce G_ATOMIC_LOCK_FREE

We clean up the detection of if we should do 'real' atomic operations or
mutex-emulated ones with the introduction of a new (public) macro:
G_ATOMIC_LOCK_FREE.  If defined, our atomic operations are guaranteed to
be done in hardware.

We need to use __GCC_HAVE_SYNC_COMPARE_AND_SWAP_4 to determine if our
compiler supports GCC-style atomic operations from the gatomic.h header
because we might be building a program against GLib using a different
set of compiler options (or a different compiler) than was used to build
GLib itself.

Unfortunately, this macro is not available on clang, so it has currently
regressed to using the mutex emulation.  A bug about that has been
opened here:

  http://llvm.org/bugs/show_bug.cgi?id=11174
This commit is contained in:
Ryan Lortie 2011-10-18 16:21:50 -04:00
parent c9b6c3c85a
commit aba0f0c38b
6 changed files with 89 additions and 50 deletions

View File

@ -2407,6 +2407,47 @@ dnl ************************
dnl *** g_atomic_* tests ***
dnl ************************
dnl We need to decide at configure time if GLib will use real atomic
dnl operations ("lock free") or emulated ones with a mutex. This is
dnl because we must put this information in glibconfig.h so we know if
dnl it is safe or not to inline using compiler intrinsics directly from
dnl the header.
dnl
dnl We also publish the information via G_ATOMIC_LOCK_FREE in case the
dnl user is interested in knowing if they can use the atomic ops across
dnl processes.
dnl
dnl We can currently support the atomic ops natively when building GLib
dnl with recent versions of GCC or MSVC. MSVC doesn't run ./configure,
dnl so we skip that case here and define G_ATOMIC_LOCK_FREE exactly when
dnl we are using GCC.
dnl
dnl Note that the atomic ops are only available with GCC on x86 when
dnl using -march=i486 or higher. If we detect that the atomic ops are
dnl not available but would be available given the right flags, we want
dnl to abort and advise the user to fix their CFLAGS. It's better to do
dnl that then to silently fall back on emulated atomic ops just because
dnl the user had the wrong build environment.
dnl We may add other compilers here in the future...
AC_MSG_CHECKING([for lock-free atomic intrinsics])
AC_TRY_COMPILE([],
[__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4;],
[g_atomic_lock_free=yes],
[g_atomic_lock_free=no])
AC_MSG_RESULT($g_atomic_lock_free)
if test "$g_atomic_lock_free" = "no"; then
SAVE_CFLAGS="${CFLAGS}"
CFLAGS="-march=i486"
AC_TRY_COMPILE([],
[__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4;],
[AC_MSG_ERROR([GLib must be build with -march=i486 or later.])],
[])
CFLAGS="${SAVE_CFLAGS}"
fi
dnl We need a more robust approach here...
case $host_cpu in
i?86|x86_64|s390|s390x|arm*|crisv32*|etrax*)
glib_memory_barrier_needed=no
@ -2419,24 +2460,6 @@ dnl ************************
;;
esac
glib_cv_gcc_has_builtin_atomic_operations=no
if test x"$GCC" = xyes; then
AC_MSG_CHECKING([whether GCC supports built-in atomic intrinsics])
AC_TRY_LINK([],
[int i;
__sync_synchronize ();
__sync_bool_compare_and_swap (&i, 0, 1);
__sync_fetch_and_add (&i, 1);
],
[glib_cv_gcc_has_builtin_atomic_operations=yes],
[glib_cv_gcc_has_builtin_atomic_operations=no])
AC_MSG_RESULT($glib_cv_gcc_has_builtin_atomic_operations)
fi
AM_CONDITIONAL(HAVE_GCC_BUILTINS_FOR_ATOMIC_OPERATIONS,
[test $glib_cv_gcc_has_builtin_atomic_operations = yes])
dnl ************************
dnl ** Check for futex(2) **
dnl ************************
@ -3085,9 +3108,9 @@ _______EOF
echo >>$outfile
echo "#define G_ATOMIC_OP_MEMORY_BARRIER_NEEDED 1" >>$outfile
fi
if test x"$g_gcc_atomic_ops" != xno; then
if test x"$g_atomic_lock_free" = xyes; then
echo >>$outfile
echo "#define G_ATOMIC_OP_USE_GCC_BUILTINS 1" >>$outfile
echo "#define G_ATOMIC_LOCK_FREE" >>$outfile
fi
echo >>$outfile
g_bit_sizes="16 32 64"

View File

@ -1,6 +1,11 @@
# This file makes most of the thread related macros look like
# functions, which they really were, if possible easy.
<MACRO>
<NAME>G_ATOMIC_LOCK_FREE</NAME>
#define G_ATOMIC_LOCK_FREE
</MACRO>
# default thread implementation
<MACRO>

View File

@ -806,6 +806,9 @@ g_async_queue_sort_unlocked
<SECTION>
<TITLE>Atomic Operations</TITLE>
<FILE>atomic_operations</FILE>
G_ATOMIC_LOCK_FREE
<SUBSECTION>
g_atomic_int_get
g_atomic_int_set
g_atomic_int_inc

View File

@ -50,22 +50,6 @@
* hardware memory barrier. Acquire and release or producer and
* consumer barrier semantics are not available through this API.
*
* On GCC, these macros are implemented using GCC intrinsic operations.
* On non-GCC compilers they will evaluate to function calls to
* functions implemented by GLib.
*
* If GLib itself was compiled with GCC then these functions will again
* be implemented by the GCC intrinsics. On Windows without GCC, the
* interlocked API is used to implement the functions.
*
* With non-GCC compilers on non-Windows systems, the functions are
* currently incapable of implementing true atomic operations --
* instead, they fallback to holding a global lock while performing the
* operation. This provides atomicity between the threads of one
* process, but not between separate processes. For this reason, one
* should exercise caution when attempting to use these options on
* shared memory regions.
*
* It is very important that all accesses to a particular integer or
* pointer be performed using only this API and that different sizes of
* operation are not mixed or used on overlapping memory regions. Never
@ -82,6 +66,19 @@
* perform the operations normally and then release the lock.
**/
/**
* G_ATOMIC_LOCK_FREE:
*
* This macro is defined if the atomic operations of GLib are
* implemented using real hardware atomic operations. This means that
* the GLib atomic API can be used between processes and safely mixed
* with other (hardware) atomic APIs.
*
* If this macro is not defined, the atomic operations may be
* emulated using a mutex. In that case, the GLib atomic operations are
* only atomic relative to themselves and within a single process.
**/
/* NOTE CAREFULLY:
*
* This file is the lowest-level part of GLib.
@ -93,12 +90,13 @@
* without risking recursion.
*/
#ifdef G_ATOMIC_OP_USE_GCC_BUILTINS
#ifdef G_ATOMIC_LOCK_FREE
#ifndef __GNUC__
#error Using GCC builtin atomic ops, but not compiling with GCC?
#endif
/* if G_ATOMIC_LOCK_FREE was defined by ./configure then we MUST
* implement the atomic operations in a lock-free manner.
*/
#if defined (__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4)
/**
* g_atomic_int_get:
* @atomic: a pointer to a #gint or #guint
@ -612,9 +610,17 @@ gsize
return InterlockedXor (atomic, val);
#endif
}
#else
/* This error occurs when ./configure decided that we should be capable
* of lock-free atomics but we find at compile-time that we are not.
*/
#error G_ATOMIC_LOCK_FREE defined, but incapable of lock-free atomics.
#endif /* defined (__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4) */
#else /* G_ATOMIC_LOCK_FREE */
/* We are not permitted to call into any GLib functions from here, so we
* can not use GMutex.
*

View File

@ -70,7 +70,7 @@ gint g_atomic_int_exchange_and_add (volatile gint *a
G_END_DECLS
#if defined(__GNUC__) && defined(G_ATOMIC_OP_USE_GCC_BUILTINS)
#if defined(G_ATOMIC_LOCK_FREE) && defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4)
#define g_atomic_int_get(atomic) \
(G_GNUC_EXTENSION ({ \
@ -177,7 +177,7 @@ G_END_DECLS
(gsize) __sync_fetch_and_xor ((atomic), (val)); \
}))
#else /* defined(__GNUC__) && defined(G_ATOMIC_OP_USE_GCC_BUILTINS) */
#else /* defined(G_ATOMIC_LOCK_FREE) && defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4) */
#define g_atomic_int_get(atomic) \
(g_atomic_int_get ((gint *) (atomic)))

View File

@ -207,6 +207,8 @@ typedef unsigned __int64 guintptr;
#define G_THREADS_ENABLED
#define G_THREADS_IMPL_WIN32
#define G_ATOMIC_LOCK_FREE
#define GINT16_TO_LE(val) ((gint16) (val))
#define GUINT16_TO_LE(val) ((guint16) (val))
#define GINT16_TO_BE(val) ((gint16) GUINT16_SWAP_LE_BE (val))