diff options
author | Ryan Lortie <desrt@desrt.ca> | 2011-10-18 16:21:50 -0400 |
---|---|---|
committer | Ryan Lortie <desrt@desrt.ca> | 2011-10-18 16:45:28 -0400 |
commit | aba0f0c38bbfa11ad48b5410ebdbed2a99e68c58 (patch) | |
tree | 7ea87ff14a2c6a2a7fba8ab7127c070e7529c0f7 | |
parent | c9b6c3c85ac8f870ff193ae75b2bd19a7a310ec9 (diff) |
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
-rw-r--r-- | configure.ac | 85 | ||||
-rw-r--r-- | docs/reference/glib/glib-overrides.txt | 5 | ||||
-rw-r--r-- | docs/reference/glib/glib-sections.txt | 3 | ||||
-rw-r--r-- | glib/gatomic.c | 48 | ||||
-rw-r--r-- | glib/gatomic.h | 4 | ||||
-rw-r--r-- | glib/glibconfig.h.win32.in | 2 |
6 files changed, 93 insertions, 54 deletions
diff --git a/configure.ac b/configure.ac index 3faaf555f..33e7005ba 100644 --- a/configure.ac +++ b/configure.ac @@ -2407,35 +2407,58 @@ dnl ************************ dnl *** g_atomic_* tests *** dnl ************************ - case $host_cpu in - i?86|x86_64|s390|s390x|arm*|crisv32*|etrax*) - glib_memory_barrier_needed=no - ;; - sparc*|alpha*|powerpc*|ia64) - glib_memory_barrier_needed=yes - ;; - *) - glib_memory_barrier_needed=yes - ;; - 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 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 + ;; + sparc*|alpha*|powerpc*|ia64) + glib_memory_barrier_needed=yes + ;; + *) + glib_memory_barrier_needed=yes + ;; +esac dnl ************************ dnl ** Check for futex(2) ** @@ -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" diff --git a/docs/reference/glib/glib-overrides.txt b/docs/reference/glib/glib-overrides.txt index 40adaded9..954dd5616 100644 --- a/docs/reference/glib/glib-overrides.txt +++ b/docs/reference/glib/glib-overrides.txt @@ -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> diff --git a/docs/reference/glib/glib-sections.txt b/docs/reference/glib/glib-sections.txt index 9fbabaf87..2e0ba3ee6 100644 --- a/docs/reference/glib/glib-sections.txt +++ b/docs/reference/glib/glib-sections.txt @@ -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 diff --git a/glib/gatomic.c b/glib/gatomic.c index 0bbe14b5f..2df2bcc3c 100644 --- a/glib/gatomic.c +++ b/glib/gatomic.c @@ -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. * diff --git a/glib/gatomic.h b/glib/gatomic.h index a02950832..376ccc185 100644 --- a/glib/gatomic.h +++ b/glib/gatomic.h @@ -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))) diff --git a/glib/glibconfig.h.win32.in b/glib/glibconfig.h.win32.in index ccd9be399..ae65d7833 100644 --- a/glib/glibconfig.h.win32.in +++ b/glib/glibconfig.h.win32.in @@ -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)) |