utils: Use GCC's __atomic built-ins if available
authorTobias Brunner <tobias@strongswan.org>
Fri, 11 Apr 2014 14:07:32 +0000 (16:07 +0200)
committerTobias Brunner <tobias@strongswan.org>
Thu, 24 Apr 2014 15:54:14 +0000 (17:54 +0200)
These are available since GCC 4.7 and will eventually replace the __sync
operations.  They support the memory model defined by C++11. For instance,
by using __ATOMIC_RELAXED for some operations on the reference counters we
can avoid memory barriers, which are required by __sync operations (whose
memory model essentially is __ATOMIC_SEQ_CST).

configure.ac
src/libstrongswan/utils/utils.c
src/libstrongswan/utils/utils.h

index 6ae0314..6999336 100644 (file)
@@ -667,21 +667,39 @@ AC_COMPILE_IFELSE(
        [AC_MSG_RESULT([no])]
 )
 
-AC_MSG_CHECKING([for gcc atomic operations])
+AC_MSG_CHECKING([for GCC __atomic operations])
 AC_RUN_IFELSE([AC_LANG_SOURCE(
        [[
                        int main() {
-                       volatile int ref = 1;
+                       int ref = 1, val;
+                       __atomic_fetch_add(&ref, 1, __ATOMIC_RELAXED);
+                       val = __atomic_sub_fetch(&ref, 1, __ATOMIC_RELAXED);
+                       __atomic_compare_exchange_n(&ref, &val, 0, 0, __ATOMIC_RELAXED,
+                                                                               __ATOMIC_RELAXED);
+                       return ref;
+               }
+       ]])],
+       [AC_MSG_RESULT([yes]);
+        AC_DEFINE([HAVE_GCC_ATOMIC_OPERATIONS], [],
+                  [have GCC __atomic_* operations])],
+       [AC_MSG_RESULT([no])],
+       [AC_MSG_RESULT([no])]
+)
+
+AC_MSG_CHECKING([for GCC __sync operations])
+AC_RUN_IFELSE([AC_LANG_SOURCE(
+       [[
+                       int main() {
+                       int ref = 1;
                        __sync_fetch_and_add (&ref, 1);
                        __sync_sub_and_fetch (&ref, 1);
-                       /* Make sure test fails if operations are not supported */
                        __sync_val_compare_and_swap(&ref, 1, 0);
                        return ref;
                }
        ]])],
        [AC_MSG_RESULT([yes]);
-        AC_DEFINE([HAVE_GCC_ATOMIC_OPERATIONS], [],
-                  [have GCC __sync_* atomic operations])],
+        AC_DEFINE([HAVE_GCC_SYNC_OPERATIONS], [],
+                  [have GCC __sync_* operations])],
        [AC_MSG_RESULT([no])],
        [AC_MSG_RESULT([no])]
 )
index e4da6dd..f2a4a06 100644 (file)
@@ -511,7 +511,7 @@ void nop()
 {
 }
 
-#ifndef HAVE_GCC_ATOMIC_OPERATIONS
+#if !defined(HAVE_GCC_ATOMIC_OPERATIONS) && !defined(HAVE_GCC_SYNC_OPERATIONS)
 
 /**
  * We use a single mutex for all refcount variables.
@@ -578,7 +578,7 @@ bool cas_##name(type *ptr, type oldval, type newval) \
 _cas_impl(bool, bool)
 _cas_impl(ptr, void*)
 
-#endif /* HAVE_GCC_ATOMIC_OPERATIONS */
+#endif /* !HAVE_GCC_ATOMIC_OPERATIONS && !HAVE_GCC_SYNC_OPERATIONS */
 
 
 #ifdef HAVE_FMEMOPEN_FALLBACK
index 4b29903..8f91e84 100644 (file)
@@ -750,6 +750,25 @@ typedef u_int refcount_t;
 
 #ifdef HAVE_GCC_ATOMIC_OPERATIONS
 
+#define ref_get(ref) __atomic_add_fetch(ref, 1, __ATOMIC_RELAXED)
+/* The relaxed memory model works fine for increments as these (usually) don't
+ * change the state of refcounted objects.  But here we have to ensure that we
+ * free the right stuff if ref counted objects are mutable.  So we have to sync
+ * with other threads that call ref_put().  It would be sufficient to use
+ * __ATOMIC_RELEASE here and then call __atomic_thread_fence() with
+ * __ATOMIC_ACQUIRE if we reach 0, but since we don't have control over the use
+ * of ref_put() we have to make sure. */
+#define ref_put(ref) (!__atomic_sub_fetch(ref, 1, __ATOMIC_ACQ_REL))
+#define ref_cur(ref) __atomic_load_n(ref, __ATOMIC_RELAXED)
+
+#define _cas_impl(ptr, oldval, newval) ({ typeof(oldval) _old = oldval; \
+                       __atomic_compare_exchange_n(ptr, &_old, newval, FALSE, \
+                                                                               __ATOMIC_SEQ_CST, __ATOMIC_RELAXED); })
+#define cas_bool(ptr, oldval, newval) _cas_impl(ptr, oldval, newval)
+#define cas_ptr(ptr, oldval, newval) _cas_impl(ptr, oldval, newval)
+
+#elif defined(HAVE_GCC_SYNC_OPERATIONS)
+
 #define ref_get(ref) __sync_add_and_fetch(ref, 1)
 #define ref_put(ref) (!__sync_sub_and_fetch(ref, 1))
 #define ref_cur(ref) __sync_fetch_and_add(ref, 0)
@@ -759,7 +778,7 @@ typedef u_int refcount_t;
 #define cas_ptr(ptr, oldval, newval) \
                                        (__sync_bool_compare_and_swap(ptr, oldval, newval))
 
-#else /* !HAVE_GCC_ATOMIC_OPERATIONS */
+#else /* !HAVE_GCC_ATOMIC_OPERATIONS && !HAVE_GCC_SYNC_OPERATIONS */
 
 /**
  * Get a new reference.