From 8aafb03dee4520ea62cd0cc0c78a9b958ec5ae18 Mon Sep 17 00:00:00 2001 From: "Andrew W. Nosenko" Date: Fri, 27 Apr 2012 17:05:32 +0300 Subject: atomic: revisit the atomic operation checks and their usage * configure.ac: Check for working Solaris/NetBSD-style atomic.h independly on OS. Add check for GCC-style __sync_*() builtins. New defines: XS_ATOMIC_GCC_SYNC, XS_ATOMIC_SOLARIS. Removed define: XS_FORCE_MUTEXES. * src/atomic_counter.hpp: (atomic_counter_t::add): (atomic_counter_t::sub): * src/atomic_ptr.hpp: (atomic_ptr_t::xchg): (atomic_ptr_t::cas): Use result of these checks. Preference order: 1. GCC-style __sync_*() builtins 2. Inline asm (x86, x86-64, armv7a) 3. Solaris/NetBSD-style atomic.h, Windows-specific API 4. Fallback to mutex-based implementation --- configure.ac | 78 ++++++++++++++++++++++--------------- src/atomic_counter.hpp | 102 +++++++++++++++++++++---------------------------- src/atomic_ptr.hpp | 95 +++++++++++++++++++-------------------------- 3 files changed, 129 insertions(+), 146 deletions(-) diff --git a/configure.ac b/configure.ac index decbd6c..0d8b404 100644 --- a/configure.ac +++ b/configure.ac @@ -106,19 +106,6 @@ AS_CASE(["${host_os}"], AC_DEFINE([XS_HAVE_SOLARIS], [1], [Have Solaris OS]) AC_CHECK_LIB([socket], [socket]) AC_CHECK_LIB([nsl], [gethostbyname]) - AC_MSG_CHECKING([whether atomic operations can be used]) - AC_COMPILE_IFELSE([AC_LANG_PROGRAM( - [[#include ]], - [[uint32_t value; - atomic_cas_32 (&value, 0, 0); - return 0;]])], - [solaris_has_atomic=yes], - [solaris_has_atomic=no]) - AC_MSG_RESULT([$solaris_has_atomic]) - # Solaris 8 does not have atomic operations exported to user space. - AS_IF([test "x$solaris_has_atomic" = "xno"], [ - AC_DEFINE([XS_FORCE_MUTEXES], [1], [Force to use mutexes]) - ]) ], [*freebsd*], [ AC_DEFINE([XS_HAVE_FREEBSD], [1], [Have FreeBSD OS]) @@ -133,24 +120,6 @@ AS_CASE(["${host_os}"], ], [*netbsd*], [ AC_DEFINE([XS_HAVE_NETBSD], [1], [Have NetBSD OS]) - # NetBSD 5.0 and newer provides atomic operations but we can - # only use these on systems where PR #42842 has been fixed so - # we must try and link a test program using C++. - libxs_netbsd_has_atomic=no - AC_MSG_CHECKING([whether atomic operations can be used]) - AC_LANG_PUSH([C++]) - AC_LINK_IFELSE([AC_LANG_PROGRAM( - [[#include ]], - [[uint32_t value; - atomic_cas_32 (&value, 0, 0); - return 0;]])], - [libxs_netbsd_has_atomic=yes], - [libxs_netbsd_has_atomic=no]) - AC_LANG_POP([C++]) - AC_MSG_RESULT([$libxs_netbsd_has_atomic]) - AS_IF([test "x$libxs_netbsd_has_atomic" = "xno"], [ - AC_DEFINE([XS_FORCE_MUTEXES], [1], [Force to use mutexes]) - ]) ], [openbsd*], [ AC_DEFINE([XS_HAVE_OPENBSD], [1], [Have OpenBSD OS]) @@ -240,6 +209,53 @@ AC_CHECK_HEADERS([ \ AC_CHECK_HEADERS([ifaddrs.h], [AC_DEFINE([XS_HAVE_IFADDRS], [1], [Have ifaddrs.h header.])]) +############################################################################### +# Check available atomic implementations +# +# We have following variants: +# o gcc __sync_*() [XS_ATOMIC_GCC_SYNC] +# o optional atomic.h with atomic_cas_32() on solaris and netbsd [XS_ATOMIC_SOLARIS] +# o windows specific [XS_HAVE_WINDOWS] +# o handwritten inline asm for x86 and arm [no specific define] +# o fallback to mutexes (or force by hands) [no specific define] + +# +# Check for Solaris and NetBSD style atomic.h +# +AC_LANG_PUSH([C++]) +AC_MSG_CHECKING([for Solaris/NetBSD-style atomic.h]) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM( + [[#include ]], + [[uint32_t value; + atomic_cas_32 (&value, 0, 0); + return 0;]])], + [solaris_style_atomic=yes], + [solaris_style_atomic=no]) +AC_MSG_RESULT([$solaris_style_atomic]) +AS_IF([test "x$solaris_style_atomic" = "xyes"], [ + AC_DEFINE([XS_ATOMIC_SOLARIS], [1], [Solaris/NetBSD-style atomic.h with atomic_cas_32()]) +]) +AC_LANG_POP([C++]) + +# +# Check for GCC-style __sync_*() builtins +# +AC_LANG_PUSH([C++]) +AC_MSG_CHECKING([for GCC-style __sync_*() atomic builtins]) +AC_LINK_IFELSE([AC_LANG_PROGRAM( + [volatile void* p;], + [[ int r; + r = __sync_bool_compare_and_swap(&p, (void*) 0x12345, (void*) 0); + return r;]])], + [gcc_style_sync_atomic=yes], + [gcc_style_sync_atomic=no]) +AC_MSG_RESULT([$gcc_style_sync_atomic]) +AS_IF([test "x$gcc_style_sync_atomic" = "xyes"], [ + AC_DEFINE([XS_ATOMIC_GCC_SYNC], [1], [GCC-style __sync_*() atomic builtins]) +]) +AC_LANG_POP([C++]) + + ############################################################################### # Check polling system # ############################################################################### diff --git a/src/atomic_counter.hpp b/src/atomic_counter.hpp index bd89f72..32395d9 100644 --- a/src/atomic_counter.hpp +++ b/src/atomic_counter.hpp @@ -25,26 +25,16 @@ #include "stdint.hpp" #include "platform.hpp" -#if defined XS_FORCE_MUTEXES -#define XS_ATOMIC_COUNTER_MUTEX -#elif (defined __i386__ || defined __x86_64__) && defined __GNUC__ -#define XS_ATOMIC_COUNTER_X86 -#elif defined __ARM_ARCH_7A__ && defined __GNUC__ -#define XS_ATOMIC_COUNTER_ARM -#elif defined XS_HAVE_WINDOWS -#define XS_ATOMIC_COUNTER_WINDOWS -#elif (defined XS_HAVE_SOLARIS || defined XS_HAVE_NETBSD) -#define XS_ATOMIC_COUNTER_ATOMIC_H +#if defined(XS_ATOMIC_GCC_SYNC) +#elif (defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))) +#elif (defined(__GNUC__) && defined(__ARM_ARCH_7A__)) +#elif defined(XS_ATOMIC_SOLARIS) +# include +#elif defined(XS_HAVE_WINDOWS) +# include "windows.hpp" #else -#define XS_ATOMIC_COUNTER_MUTEX -#endif - -#if defined XS_ATOMIC_COUNTER_MUTEX -#include "mutex.hpp" -#elif defined XS_ATOMIC_COUNTER_WINDOWS -#include "windows.hpp" -#elif defined XS_ATOMIC_COUNTER_ATOMIC_H -#include +# define XS_ATOMIC_OVER_MUTEX 1 +# include "mutex.hpp" #endif namespace xs @@ -79,20 +69,17 @@ namespace xs { integer_t old_value; -#if defined XS_ATOMIC_COUNTER_WINDOWS - old_value = InterlockedExchangeAdd ((LONG*) &value, increment_); -#elif defined __GNUC__ && !defined XS_DISABLE_GCC_SYNC_BUILTINS +#if defined(XS_ATOMIC_GCC_SYNC) old_value = __sync_fetch_and_add (&value, increment_); -#elif defined XS_ATOMIC_COUNTER_ATOMIC_H - integer_t new_value = atomic_add_32_nv (&value, increment_); - old_value = new_value - increment_; -#elif defined XS_ATOMIC_COUNTER_X86 + +#elif (defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))) __asm__ volatile ( "lock; xadd %0, %1 \n\t" : "=r" (old_value), "=m" (value) : "0" (increment_), "m" (value) : "cc", "memory"); -#elif defined XS_ATOMIC_COUNTER_ARM + +#elif (defined(__GNUC__) && defined(__ARM_ARCH_7A__)) integer_t flag, tmp; __asm__ volatile ( " dmb sy\n\t" @@ -105,13 +92,19 @@ namespace xs : "=&r"(old_value), "=&r"(flag), "=&r"(tmp), "+Qo"(value) : "Ir"(increment_), "r"(&value) : "cc"); -#elif defined XS_ATOMIC_COUNTER_MUTEX + +#elif defined(XS_ATOMIC_SOLARIS) + integer_t new_value = atomic_add_32_nv (&value, increment_); + old_value = new_value - increment_; + +#elif defined(XS_HAVE_WINDOWS) + old_value = InterlockedExchangeAdd ((LONG*) &value, increment_); + +#else sync.lock (); old_value = value; value += increment_; sync.unlock (); -#else -#error atomic_counter is not implemented for this platform #endif return old_value; } @@ -119,18 +112,11 @@ namespace xs // Atomic subtraction. Returns false if the counter drops to zero. inline bool sub (integer_t decrement) { -#if defined XS_ATOMIC_COUNTER_WINDOWS - LONG delta = - ((LONG) decrement); - integer_t old = InterlockedExchangeAdd ((LONG*) &value, delta); - return old - decrement != 0; -#elif defined __GNUC__ && !defined XS_DISABLE_GCC_SYNC_BUILTINS +#if defined(XS_ATOMIC_GCC_SYNC) integer_t new_value = __sync_sub_and_fetch (&value, decrement); return (new_value != 0); -#elif defined XS_ATOMIC_COUNTER_ATOMIC_H - int32_t delta = - ((int32_t) decrement); - integer_t nv = atomic_add_32_nv (&value, delta); - return nv != 0; -#elif defined XS_ATOMIC_COUNTER_X86 + +#elif (defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))) integer_t oldval = -decrement; volatile integer_t *val = &value; __asm__ volatile ("lock; xaddl %0,%1" @@ -138,7 +124,8 @@ namespace xs : "0" (oldval), "m" (*val) : "cc", "memory"); return oldval != decrement; -#elif defined XS_ATOMIC_COUNTER_ARM + +#elif (defined(__GNUC__) && defined(__ARM_ARCH_7A__)) integer_t old_value, flag, tmp; __asm__ volatile ( " dmb sy\n\t" @@ -152,14 +139,23 @@ namespace xs : "Ir"(decrement), "r"(&value) : "cc"); return old_value - decrement != 0; -#elif defined XS_ATOMIC_COUNTER_MUTEX + +#elif defined(XS_ATOMIC_SOLARIS) + int32_t delta = - ((int32_t) decrement); + integer_t nv = atomic_add_32_nv (&value, delta); + return nv != 0; + +#elif defined(XS_HAVE_WINDOWS) + LONG delta = - ((LONG) decrement); + integer_t old = InterlockedExchangeAdd ((LONG*) &value, delta); + return old - decrement != 0; + +#else sync.lock (); value -= decrement; bool result = value ? true : false; sync.unlock (); return result; -#else -#error atomic_counter is not implemented for this platform #endif } @@ -171,7 +167,7 @@ namespace xs private: volatile integer_t value; -#if defined XS_ATOMIC_COUNTER_MUTEX +#if defined(XS_ATOMIC_OVER_MUTEX) mutex_t sync; #endif @@ -182,20 +178,8 @@ namespace xs } // Remove macros local to this file. -#if defined XS_ATOMIC_COUNTER_WINDOWS -#undef XS_ATOMIC_COUNTER_WINDOWS -#endif -#if defined XS_ATOMIC_COUNTER_ATOMIC_H -#undef XS_ATOMIC_COUNTER_ATOMIC_H -#endif -#if defined XS_ATOMIC_COUNTER_X86 -#undef XS_ATOMIC_COUNTER_X86 -#endif -#if defined XS_ATOMIC_COUNTER_ARM -#undef XS_ATOMIC_COUNTER_ARM -#endif -#if defined XS_ATOMIC_COUNTER_MUTEX -#undef XS_ATOMIC_COUNTER_MUTEX +#if defined(XS_ATOMIC_OVER_MUTEX) +# undef XS_ATOMIC_OVER_MUTEX #endif #endif diff --git a/src/atomic_ptr.hpp b/src/atomic_ptr.hpp index 2e585b7..08418d3 100644 --- a/src/atomic_ptr.hpp +++ b/src/atomic_ptr.hpp @@ -26,26 +26,16 @@ #include "platform.hpp" -#if defined XS_FORCE_MUTEXES -#define XS_ATOMIC_PTR_MUTEX -#elif (defined __i386__ || defined __x86_64__) && defined __GNUC__ -#define XS_ATOMIC_PTR_X86 -#elif defined __ARM_ARCH_7A__ && defined __GNUC__ -#define XS_ATOMIC_PTR_ARM -#elif defined XS_HAVE_WINDOWS -#define XS_ATOMIC_PTR_WINDOWS -#elif (defined XS_HAVE_SOLARIS || defined XS_HAVE_NETBSD) -#define XS_ATOMIC_PTR_ATOMIC_H +#if defined(XS_ATOMIC_GCC_SYNC) +#elif (defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))) +#elif (defined(__GNUC__) && defined(__ARM_ARCH_7A__)) +#elif defined(XS_ATOMIC_SOLARIS) +# include +#elif defined(XS_HAVE_WINDOWS) +# include "windows.hpp" #else -#define XS_ATOMIC_PTR_MUTEX -#endif - -#if defined XS_ATOMIC_PTR_MUTEX -#include "mutex.hpp" -#elif defined XS_ATOMIC_PTR_WINDOWS -#include "windows.hpp" -#elif defined XS_ATOMIC_PTR_ATOMIC_H -#include +# define XS_ATOMIC_OVER_MUTEX 1 +# include "mutex.hpp" #endif namespace xs @@ -80,9 +70,7 @@ namespace xs // to the 'val' value. Old value is returned. inline T *xchg (T *val_) { -#if defined XS_ATOMIC_PTR_WINDOWS - return (T*) InterlockedExchangePointer ((PVOID*) &ptr, val_); -#elif defined __GNUC__ && !defined XS_DISABLE_GCC_SYNC_BUILTINS +#if defined(XS_ATOMIC_GCC_SYNC) { T* ov; do @@ -91,16 +79,16 @@ namespace xs } while (!__sync_bool_compare_and_swap (&ptr, ov, val_)); return ov; } -#elif defined XS_ATOMIC_PTR_ATOMIC_H - return (T*) atomic_swap_ptr (&ptr, val_); -#elif defined XS_ATOMIC_PTR_X86 + +#elif (defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))) T *old; __asm__ volatile ( "lock; xchg %0, %2" : "=r" (old), "=m" (ptr) : "m" (ptr), "0" (val_)); return old; -#elif defined XS_ATOMIC_PTR_ARM + +#elif (defined(__GNUC__) && defined(__ARM_ARCH_7A__)) T* old; unsigned int flag; __asm__ volatile ( @@ -114,14 +102,19 @@ namespace xs : "r"(&ptr), "r"(val_) : "cc"); return old; -#elif defined XS_ATOMIC_PTR_MUTEX + +#elif defined(XS_ATOMIC_SOLARIS) + return (T*) atomic_swap_ptr (&ptr, val_); + +#elif defined(XS_HAVE_WINDOWS) + return (T*) InterlockedExchangePointer ((PVOID*) &ptr, val_); + +#else sync.lock (); T *old = (T*) ptr; ptr = val_; sync.unlock (); return old; -#else -#error atomic_ptr is not implemented for this platform #endif } @@ -131,14 +124,10 @@ namespace xs // is returned. inline T *cas (T *cmp_, T *val_) { -#if defined XS_ATOMIC_PTR_WINDOWS - return (T*) InterlockedCompareExchangePointer ( - (volatile PVOID*) &ptr, val_, cmp_); -#elif defined __GNUC__ && !defined XS_DISABLE_GCC_SYNC_BUILTINS +#if defined(XS_ATOMIC_GCC_SYNC) return (T*) __sync_val_compare_and_swap (&ptr, cmp_, val_); -#elif defined XS_ATOMIC_PTR_ATOMIC_H - return (T*) atomic_cas_ptr (&ptr, cmp_, val_); -#elif defined XS_ATOMIC_PTR_X86 + +#elif (defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))) T *old; __asm__ volatile ( "lock; cmpxchg %2, %3" @@ -146,7 +135,8 @@ namespace xs : "r" (val_), "m" (ptr), "0" (cmp_) : "cc"); return old; -#elif defined XS_ATOMIC_PTR_ARM + +#elif (defined(__GNUC__) && defined(__ARM_ARCH_7A__)) T *old; unsigned int flag; __asm__ volatile ( @@ -162,22 +152,28 @@ namespace xs : "r"(&ptr), "r"(cmp_), "r"(val_) : "cc"); return old; -#elif defined XS_ATOMIC_PTR_MUTEX + +#elif defined(XS_ATOMIC_SOLARIS) + return (T*) atomic_cas_ptr (&ptr, cmp_, val_); + +#elif defined(XS_HAVE_WINDOWS) + return (T*) InterlockedCompareExchangePointer ( + (volatile PVOID*) &ptr, val_, cmp_); + +#else sync.lock (); T *old = (T*) ptr; if (ptr == cmp_) ptr = val_; sync.unlock (); return old; -#else -#error atomic_ptr is not implemented for this platform #endif } private: volatile T *ptr; -#if defined XS_ATOMIC_PTR_MUTEX +#if defined XS_ATOMIC_OVER_MUTEX mutex_t sync; #endif @@ -188,21 +184,8 @@ namespace xs } // Remove macros local to this file. -#if defined XS_ATOMIC_PTR_WINDOWS -#undef XS_ATOMIC_PTR_WINDOWS -#endif -#if defined XS_ATOMIC_PTR_ATOMIC_H -#undef XS_ATOMIC_PTR_ATOMIC_H -#endif -#if defined XS_ATOMIC_PTR_X86 -#undef XS_ATOMIC_PTR_X86 -#endif -#if defined XS_ATOMIC_PTR_ARM -#undef XS_ATOMIC_PTR_ARM -#endif -#if defined XS_ATOMIC_PTR_MUTEX -#undef XS_ATOMIC_PTR_MUTEX +#if defined(XS_ATOMIC_OVER_MUTEX) +# undef XS_ATOMIC_OVER_MUTEX #endif #endif - -- cgit v1.2.3