[glib] gatomic: introduce G_ATOMIC_LOCK_FREE



commit aba0f0c38bbfa11ad48b5410ebdbed2a99e68c58
Author: Ryan Lortie <desrt desrt ca>
Date:   Tue Oct 18 16:21:50 2011 -0400

    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

 configure.ac                           |   85 ++++++++++++++++++++------------
 docs/reference/glib/glib-overrides.txt |    5 ++
 docs/reference/glib/glib-sections.txt  |    3 +
 glib/gatomic.c                         |   48 ++++++++++--------
 glib/gatomic.h                         |    4 +-
 glib/glibconfig.h.win32.in             |    2 +
 6 files changed, 93 insertions(+), 54 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 3faaf55..33e7005 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 40adade..954dd56 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 9fbabaf..2e0ba3e 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 0bbe14b..2df2bcc 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 a029508..376ccc1 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 ccd9be3..ae65d78 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))



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]