Commit 382ae222 authored by Marc Alff's avatar Marc Alff

WL#2595 kernel-independent atomic operations

Backport from 6.0.14 to 5.6.0

Original code from Sergei Golubchik
parent 7d598780
...@@ -1750,23 +1750,29 @@ then ...@@ -1750,23 +1750,29 @@ then
fi fi
AC_ARG_WITH([atomic-ops], AC_ARG_WITH([atomic-ops],
AC_HELP_STRING([--with-atomic-ops=rwlocks|smp|up], AS_HELP_STRING([--with-atomic-ops=rwlocks|smp|up],
[Implement atomic operations using pthread rwlocks or atomic CPU [Implement atomic operations using pthread rwlocks or atomic CPU
instructions for multi-processor (default) or uniprocessor instructions for multi-processor or uniprocessor
configuration]), , [with_atomic_ops=smp]) configuration. By default gcc built-in sync functions are used,
if available and 'smp' configuration otherwise.]))
case "$with_atomic_ops" in case "$with_atomic_ops" in
"up") AC_DEFINE([MY_ATOMIC_MODE_DUMMY], [1], "up") AC_DEFINE([MY_ATOMIC_MODE_DUMMY], [1],
[Assume single-CPU mode, no concurrency]) ;; [Assume single-CPU mode, no concurrency]) ;;
"rwlocks") AC_DEFINE([MY_ATOMIC_MODE_RWLOCKS], [1], "rwlocks") AC_DEFINE([MY_ATOMIC_MODE_RWLOCKS], [1],
[Use pthread rwlocks for atomic ops]) ;; [Use pthread rwlocks for atomic ops]) ;;
"smp") ;; "smp") ;;
"")
;;
*) AC_MSG_ERROR(["$with_atomic_ops" is not a valid value for --with-atomic-ops]) ;; *) AC_MSG_ERROR(["$with_atomic_ops" is not a valid value for --with-atomic-ops]) ;;
esac esac
AC_CACHE_CHECK([whether the compiler provides atomic builtins], AC_CACHE_CHECK([whether the compiler provides atomic builtins],
[mysql_cv_gcc_atomic_builtins], [AC_TRY_RUN([ [mysql_cv_gcc_atomic_builtins],
int main() [AC_RUN_IFELSE(
{ [AC_LANG_PROGRAM(
[
],
[[
int foo= -10; int bar= 10; int foo= -10; int bar= 10;
if (!__sync_fetch_and_add(&foo, bar) || foo) if (!__sync_fetch_and_add(&foo, bar) || foo)
return -1; return -1;
...@@ -1777,22 +1783,25 @@ AC_CACHE_CHECK([whether the compiler provides atomic builtins], ...@@ -1777,22 +1783,25 @@ AC_CACHE_CHECK([whether the compiler provides atomic builtins],
if (bar) if (bar)
return -1; return -1;
return 0; return 0;
} ]]
], [mysql_cv_gcc_atomic_builtins=yes], )],
[mysql_cv_gcc_atomic_builtins=yes],
[mysql_cv_gcc_atomic_builtins=no], [mysql_cv_gcc_atomic_builtins=no],
[mysql_cv_gcc_atomic_builtins=no])]) [mysql_cv_gcc_atomic_builtins=no]
)])
if test "x$mysql_cv_gcc_atomic_builtins" = xyes; then if test "x$mysql_cv_gcc_atomic_builtins" = xyes; then
AC_DEFINE(HAVE_GCC_ATOMIC_BUILTINS, 1, AC_DEFINE(HAVE_GCC_ATOMIC_BUILTINS, 1,
[Define to 1 if compiler provides atomic builtins.]) [Define to 1 if compiler provides atomic builtins.])
fi fi
AC_CACHE_CHECK([whether the OS provides atomic_* functions like Solaris], AC_CACHE_CHECK([whether the OS provides atomic_* functions like Solaris],
[mysql_cv_solaris_atomic], [AC_TRY_RUN([ [mysql_cv_solaris_atomic],
#include <atomic.h> [AC_RUN_IFELSE(
int [AC_LANG_PROGRAM(
main() [
{ #include <atomic.h>
]
[[
int foo = -10; int bar = 10; int foo = -10; int bar = 10;
if (atomic_add_int_nv((uint_t *)&foo, bar) || foo) if (atomic_add_int_nv((uint_t *)&foo, bar) || foo)
return -1; return -1;
...@@ -1803,11 +1812,12 @@ main() ...@@ -1803,11 +1812,12 @@ main()
if (bar) if (bar)
return -1; return -1;
return 0; return 0;
} ]]
], [mysql_cv_solaris_atomic=yes], )],
[mysql_cv_solaris_atomic=yes],
[mysql_cv_solaris_atomic=no], [mysql_cv_solaris_atomic=no],
[mysql_cv_solaris_atomic=no])]) [mysql_cv_solaris_atomic=no]
)])
if test "x$mysql_cv_solaris_atomic" = xyes; then if test "x$mysql_cv_solaris_atomic" = xyes; then
AC_DEFINE(HAVE_SOLARIS_ATOMIC, 1, AC_DEFINE(HAVE_SOLARIS_ATOMIC, 1,
[Define to 1 if OS provides atomic_* functions like Solaris.]) [Define to 1 if OS provides atomic_* functions like Solaris.])
......
...@@ -39,9 +39,9 @@ noinst_HEADERS = config-win.h config-netware.h my_bit.h \ ...@@ -39,9 +39,9 @@ noinst_HEADERS = config-win.h config-netware.h my_bit.h \
thr_lock.h t_ctype.h violite.h my_md5.h base64.h \ thr_lock.h t_ctype.h violite.h my_md5.h base64.h \
my_handler.h my_time.h service_versions.h \ my_handler.h my_time.h service_versions.h \
my_vle.h my_user.h my_atomic.h atomic/nolock.h \ my_vle.h my_user.h my_atomic.h atomic/nolock.h \
atomic/rwlock.h atomic/x86-gcc.h atomic/x86-msvc.h \ atomic/rwlock.h atomic/x86-gcc.h atomic/generic-msvc.h \
atomic/solaris.h \ atomic/gcc_builtins.h my_libwrap.h my_stacktrace.h \
atomic/gcc_builtins.h my_libwrap.h my_stacktrace.h atomic/solaris.h
EXTRA_DIST = mysql.h.pp mysql/plugin.h.pp probes_mysql.d.base EXTRA_DIST = mysql.h.pp mysql/plugin.h.pp probes_mysql.d.base
......
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
#define make_atomic_add_body(S) \ #define make_atomic_add_body(S) \
v= __sync_fetch_and_add(a, v); v= __sync_fetch_and_add(a, v);
#define make_atomic_swap_body(S) \ #define make_atomic_fas_body(S) \
v= __sync_lock_test_and_set(a, v); v= __sync_lock_test_and_set(a, v);
#define make_atomic_cas_body(S) \ #define make_atomic_cas_body(S) \
int ## S sav; \ int ## S sav; \
...@@ -28,7 +28,10 @@ ...@@ -28,7 +28,10 @@
#ifdef MY_ATOMIC_MODE_DUMMY #ifdef MY_ATOMIC_MODE_DUMMY
#define make_atomic_load_body(S) ret= *a #define make_atomic_load_body(S) ret= *a
#define make_atomic_store_body(S) *a= v #define make_atomic_store_body(S) *a= v
#define MY_ATOMIC_MODE "gcc-builtins-up"
#else #else
#define MY_ATOMIC_MODE "gcc-builtins-smp"
#define make_atomic_load_body(S) \ #define make_atomic_load_body(S) \
ret= __sync_fetch_and_or(a, 0); ret= __sync_fetch_and_or(a, 0);
#define make_atomic_store_body(S) \ #define make_atomic_store_body(S) \
......
/* Copyright (C) 2006-2008 MySQL AB, 2008-2009 Sun Microsystems, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#ifndef _atomic_h_cleanup_
#define _atomic_h_cleanup_ "atomic/generic-msvc.h"
/*
We don't implement anything specific for MY_ATOMIC_MODE_DUMMY, always use
intrinsics.
8 and 16-bit atomics are not implemented, but it can be done if necessary.
*/
#undef MY_ATOMIC_HAS_8_16
/*
x86 compilers (both VS2003 or VS2005) never use instrinsics, but generate
function calls to kernel32 instead, even in the optimized build.
We force intrinsics as described in MSDN documentation for
_InterlockedCompareExchange.
*/
#ifdef _M_IX86
#if (_MSC_VER >= 1500)
#include <intrin.h>
#else
C_MODE_START
/*Visual Studio 2003 and earlier do not have prototypes for atomic intrinsics*/
LONG _InterlockedExchange (LONG volatile *Target,LONG Value);
LONG _InterlockedCompareExchange (LONG volatile *Target, LONG Value, LONG Comp);
LONG _InterlockedExchangeAdd (LONG volatile *Addend, LONG Value);
C_MODE_END
#pragma intrinsic(_InterlockedExchangeAdd)
#pragma intrinsic(_InterlockedCompareExchange)
#pragma intrinsic(_InterlockedExchange)
#endif
#define InterlockedExchange _InterlockedExchange
#define InterlockedExchangeAdd _InterlockedExchangeAdd
#define InterlockedCompareExchange _InterlockedCompareExchange
/*
No need to do something special for InterlockedCompareExchangePointer
as it is a #define to InterlockedCompareExchange. The same applies to
InterlockedExchangePointer.
*/
#endif /*_M_IX86*/
#define MY_ATOMIC_MODE "msvc-intrinsics"
#define IL_EXCHG_ADD32(X,Y) InterlockedExchangeAdd((volatile LONG *)(X),(Y))
#define IL_COMP_EXCHG32(X,Y,Z) InterlockedCompareExchange((volatile LONG *)(X),(Y),(Z))
#define IL_COMP_EXCHGptr InterlockedCompareExchangePointer
#define IL_EXCHG32(X,Y) InterlockedExchange((volatile LONG *)(X),(Y))
#define IL_EXCHGptr InterlockedExchangePointer
#define make_atomic_add_body(S) \
v= IL_EXCHG_ADD ## S (a, v)
#define make_atomic_cas_body(S) \
int ## S initial_cmp= *cmp; \
int ## S initial_a= IL_COMP_EXCHG ## S (a, set, initial_cmp); \
if (!(ret= (initial_a == initial_cmp))) *cmp= initial_a;
#define make_atomic_swap_body(S) \
v= IL_EXCHG ## S (a, v)
#define make_atomic_load_body(S) \
ret= 0; /* avoid compiler warning */ \
ret= IL_COMP_EXCHG ## S (a, ret, ret);
/*
my_yield_processor (equivalent of x86 PAUSE instruction) should be used
to improve performance on hyperthreaded CPUs. Intel recommends to use it in
spin loops also on non-HT machines to reduce power consumption (see e.g
http://softwarecommunity.intel.com/articles/eng/2004.htm)
Running benchmarks for spinlocks implemented with InterlockedCompareExchange
and YieldProcessor shows that much better performance is achieved by calling
YieldProcessor in a loop - that is, yielding longer. On Intel boxes setting
loop count in the range 200-300 brought best results.
*/
#ifndef YIELD_LOOPS
#define YIELD_LOOPS 200
#endif
static __inline int my_yield_processor()
{
int i;
for(i=0; i<YIELD_LOOPS; i++)
{
#if (_MSC_VER <= 1310)
/* On older compilers YieldProcessor is not available, use inline assembly*/
__asm { rep nop }
#else
YieldProcessor();
#endif
}
return 1;
}
#define LF_BACKOFF my_yield_processor()
#else /* cleanup */
#undef IL_EXCHG_ADD32
#undef IL_COMP_EXCHG32
#undef IL_COMP_EXCHGptr
#undef IL_EXCHG32
#undef IL_EXCHGptr
#endif
...@@ -16,43 +16,36 @@ ...@@ -16,43 +16,36 @@
along with this program; if not, write to the Free Software along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#if defined(__i386__) || defined(_M_IX86) || defined(HAVE_GCC_ATOMIC_BUILTINS) #if defined(__i386__) || defined(_MSC_VER) || defined(__x86_64__) \
|| defined(HAVE_GCC_ATOMIC_BUILTINS)
#ifdef MY_ATOMIC_MODE_DUMMY
# define LOCK "" # ifdef MY_ATOMIC_MODE_DUMMY
#else # define LOCK_prefix ""
# define LOCK "lock" # else
#endif # define LOCK_prefix "lock"
# endif
#ifdef HAVE_GCC_ATOMIC_BUILTINS
#include "gcc_builtins.h" # ifdef HAVE_GCC_ATOMIC_BUILTINS
#elif __GNUC__ # include "gcc_builtins.h"
#include "x86-gcc.h" # elif __GNUC__
#elif defined(_MSC_VER) # include "x86-gcc.h"
#include "x86-msvc.h" # elif defined(_MSC_VER)
#endif # include "generic-msvc.h"
# endif
#elif defined(HAVE_SOLARIS_ATOMIC) #elif defined(HAVE_SOLARIS_ATOMIC)
#include "solaris.h" #include "solaris.h"
#endif
#endif /* __i386__ || _M_IX86 || HAVE_GCC_ATOMIC_BUILTINS */
#if defined(make_atomic_cas_body) || defined(MY_ATOMICS_MADE) #if defined(make_atomic_cas_body) || defined(MY_ATOMICS_MADE)
/* /*
* We have atomics that require no locking * We have atomics that require no locking
*/ */
#define MY_ATOMIC_NOLOCK #define MY_ATOMIC_NOLOCK
#ifdef __SUNPRO_C
/* /*
* Sun Studio 12 (and likely earlier) does not accept a typedef struct {} Type not used so minimal size (emptry struct has different size between C
*/ and C++, zero-length array is gcc-specific).
typedef char my_atomic_rwlock_t; */
#else typedef char my_atomic_rwlock_t __attribute__ ((unused));
typedef struct { } my_atomic_rwlock_t;
#endif
#define my_atomic_rwlock_destroy(name) #define my_atomic_rwlock_destroy(name)
#define my_atomic_rwlock_init(name) #define my_atomic_rwlock_init(name)
#define my_atomic_rwlock_rdlock(name) #define my_atomic_rwlock_rdlock(name)
......
#ifndef ATOMIC_RWLOCK_INCLUDED #ifndef ATOMIC_RWLOCK_INCLUDED
#define ATOMIC_RWLOCK_INCLUDED #define ATOMIC_RWLOCK_INCLUDED
/* Copyright (C) 2006 MySQL AB /* Copyright (C) 2006 MySQL AB, 2009 Sun Microsystems, Inc.
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
along with this program; if not, write to the Free Software along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
typedef struct {pthread_rwlock_t rw;} my_atomic_rwlock_t; #define MY_ATOMIC_MODE_RWLOCKS 1
#ifdef MY_ATOMIC_MODE_DUMMY #ifdef MY_ATOMIC_MODE_DUMMY
/* /*
...@@ -26,6 +26,9 @@ typedef struct {pthread_rwlock_t rw;} my_atomic_rwlock_t; ...@@ -26,6 +26,9 @@ typedef struct {pthread_rwlock_t rw;} my_atomic_rwlock_t;
implementations (another way is to run a UP build on an SMP box). implementations (another way is to run a UP build on an SMP box).
*/ */
#warning MY_ATOMIC_MODE_DUMMY and MY_ATOMIC_MODE_RWLOCKS are incompatible #warning MY_ATOMIC_MODE_DUMMY and MY_ATOMIC_MODE_RWLOCKS are incompatible
typedef char my_atomic_rwlock_t;
#define my_atomic_rwlock_destroy(name) #define my_atomic_rwlock_destroy(name)
#define my_atomic_rwlock_init(name) #define my_atomic_rwlock_init(name)
#define my_atomic_rwlock_rdlock(name) #define my_atomic_rwlock_rdlock(name)
...@@ -33,18 +36,63 @@ typedef struct {pthread_rwlock_t rw;} my_atomic_rwlock_t; ...@@ -33,18 +36,63 @@ typedef struct {pthread_rwlock_t rw;} my_atomic_rwlock_t;
#define my_atomic_rwlock_rdunlock(name) #define my_atomic_rwlock_rdunlock(name)
#define my_atomic_rwlock_wrunlock(name) #define my_atomic_rwlock_wrunlock(name)
#define MY_ATOMIC_MODE "dummy (non-atomic)" #define MY_ATOMIC_MODE "dummy (non-atomic)"
#else #else /* not MY_ATOMIC_MODE_DUMMY */
#define my_atomic_rwlock_destroy(name) pthread_rwlock_destroy(& (name)->rw)
#define my_atomic_rwlock_init(name) pthread_rwlock_init(& (name)->rw, 0) typedef struct {pthread_mutex_t rw;} my_atomic_rwlock_t;
#define my_atomic_rwlock_rdlock(name) pthread_rwlock_rdlock(& (name)->rw)
#define my_atomic_rwlock_wrlock(name) pthread_rwlock_wrlock(& (name)->rw) #ifndef SAFE_MUTEX
#define my_atomic_rwlock_rdunlock(name) pthread_rwlock_unlock(& (name)->rw)
#define my_atomic_rwlock_wrunlock(name) pthread_rwlock_unlock(& (name)->rw) /*
#define MY_ATOMIC_MODE "rwlocks" we're using read-write lock macros but map them to mutex locks, and they're
faster. Still, having semantically rich API we can change the
underlying implementation, if necessary.
*/
#define my_atomic_rwlock_destroy(name) pthread_mutex_destroy(& (name)->rw)
#define my_atomic_rwlock_init(name) pthread_mutex_init(& (name)->rw, 0)
#define my_atomic_rwlock_rdlock(name) pthread_mutex_lock(& (name)->rw)
#define my_atomic_rwlock_wrlock(name) pthread_mutex_lock(& (name)->rw)
#define my_atomic_rwlock_rdunlock(name) pthread_mutex_unlock(& (name)->rw)
#define my_atomic_rwlock_wrunlock(name) pthread_mutex_unlock(& (name)->rw)
#else /* SAFE_MUTEX */
/*
SAFE_MUTEX pollutes the compiling name space with macros
that alter pthread_mutex_t, pthread_mutex_init, etc.
Atomic operations should never use the safe mutex wrappers.
Unfortunately, there is no way to have both:
- safe mutex macros expanding pthread_mutex_lock to safe_mutex_lock
- my_atomic macros expanding to unmodified pthread_mutex_lock
inlined in the same compilation unit.
So, in case of SAFE_MUTEX, a function call is required.
Given that SAFE_MUTEX is a debugging facility,
this extra function call is not a performance concern for
production builds.
*/
C_MODE_START
extern void plain_pthread_mutex_init(safe_mutex_t *);
extern void plain_pthread_mutex_destroy(safe_mutex_t *);
extern void plain_pthread_mutex_lock(safe_mutex_t *);
extern void plain_pthread_mutex_unlock(safe_mutex_t *);
C_MODE_END
#define my_atomic_rwlock_destroy(name) plain_pthread_mutex_destroy(&(name)->rw)
#define my_atomic_rwlock_init(name) plain_pthread_mutex_init(&(name)->rw)
#define my_atomic_rwlock_rdlock(name) plain_pthread_mutex_lock(&(name)->rw)
#define my_atomic_rwlock_wrlock(name) plain_pthread_mutex_lock(&(name)->rw)
#define my_atomic_rwlock_rdunlock(name) plain_pthread_mutex_unlock(&(name)->rw)
#define my_atomic_rwlock_wrunlock(name) plain_pthread_mutex_unlock(&(name)->rw)
#endif /* SAFE_MUTEX */
#define MY_ATOMIC_MODE "mutex"
#ifndef MY_ATOMIC_MODE_RWLOCKS
#define MY_ATOMIC_MODE_RWLOCKS 1
#endif
#endif #endif
#define make_atomic_add_body(S) int ## S sav; sav= *a; *a+= v; v=sav; #define make_atomic_add_body(S) int ## S sav; sav= *a; *a+= v; v=sav;
#define make_atomic_swap_body(S) int ## S sav; sav= *a; *a= v; v=sav; #define make_atomic_fas_body(S) int ## S sav; sav= *a; *a= v; v=sav;
#define make_atomic_cas_body(S) if ((ret= (*a == *cmp))) *a= set; else *cmp=*a; #define make_atomic_cas_body(S) if ((ret= (*a == *cmp))) *a= set; else *cmp=*a;
#define make_atomic_load_body(S) ret= *a; #define make_atomic_load_body(S) ret= *a;
#define make_atomic_store_body(S) *a= v; #define make_atomic_store_body(S) *a= v;
......
...@@ -186,25 +186,25 @@ my_atomic_storeptr(void * volatile *a, void *v) ...@@ -186,25 +186,25 @@ my_atomic_storeptr(void * volatile *a, void *v)
/* ------------------------------------------------------------------------ */ /* ------------------------------------------------------------------------ */
STATIC_INLINE int8 STATIC_INLINE int8
my_atomic_swap8(int8 volatile *a, int8 v) my_atomic_fas8(int8 volatile *a, int8 v)
{ {
return ((int8) atomic_swap_8((volatile uint8_t *)a, (uint8_t)v)); return ((int8) atomic_swap_8((volatile uint8_t *)a, (uint8_t)v));
} }
STATIC_INLINE int16 STATIC_INLINE int16
my_atomic_swap16(int16 volatile *a, int16 v) my_atomic_fas16(int16 volatile *a, int16 v)
{ {
return ((int16) atomic_swap_16((volatile uint16_t *)a, (uint16_t)v)); return ((int16) atomic_swap_16((volatile uint16_t *)a, (uint16_t)v));
} }
STATIC_INLINE int32 STATIC_INLINE int32
my_atomic_swap32(int32 volatile *a, int32 v) my_atomic_fas32(int32 volatile *a, int32 v)
{ {
return ((int32) atomic_swap_32((volatile uint32_t *)a, (uint32_t)v)); return ((int32) atomic_swap_32((volatile uint32_t *)a, (uint32_t)v));
} }
STATIC_INLINE void * STATIC_INLINE void *
my_atomic_swapptr(void * volatile *a, void *v) my_atomic_fasptr(void * volatile *a, void *v)
{ {
return (atomic_swap_ptr(a, v)); return (atomic_swap_ptr(a, v));
} }
...@@ -22,10 +22,18 @@ ...@@ -22,10 +22,18 @@
architectures support double-word (128-bit) cas. architectures support double-word (128-bit) cas.
*/ */
#ifdef MY_ATOMIC_NO_XADD #ifdef __x86_64__
#define MY_ATOMIC_MODE "gcc-x86" LOCK "-no-xadd" # ifdef MY_ATOMIC_NO_XADD
# define MY_ATOMIC_MODE "gcc-amd64" LOCK_prefix "-no-xadd"
# else
# define MY_ATOMIC_MODE "gcc-amd64" LOCK_prefix
# endif
#else #else
#define MY_ATOMIC_MODE "gcc-x86" LOCK # ifdef MY_ATOMIC_NO_XADD
# define MY_ATOMIC_MODE "gcc-x86" LOCK_prefix "-no-xadd"
# else
# define MY_ATOMIC_MODE "gcc-x86" LOCK_prefix
# endif
#endif #endif
/* fix -ansi errors while maintaining readability */ /* fix -ansi errors while maintaining readability */
...@@ -35,12 +43,12 @@ ...@@ -35,12 +43,12 @@
#ifndef MY_ATOMIC_NO_XADD #ifndef MY_ATOMIC_NO_XADD
#define make_atomic_add_body(S) \ #define make_atomic_add_body(S) \
asm volatile (LOCK "; xadd %0, %1;" : "+r" (v) , "+m" (*a)) asm volatile (LOCK_prefix "; xadd %0, %1;" : "+r" (v) , "+m" (*a))
#endif #endif
#define make_atomic_swap_body(S) \ #define make_atomic_fas_body(S) \
asm volatile ("; xchg %0, %1;" : "+q" (v) , "+m" (*a)) asm volatile ("xchg %0, %1;" : "+q" (v) , "+m" (*a))
#define make_atomic_cas_body(S) \ #define make_atomic_cas_body(S) \
asm volatile (LOCK "; cmpxchg %3, %0; setz %2;" \ asm volatile (LOCK_prefix "; cmpxchg %3, %0; setz %2;" \
: "+m" (*a), "+a" (*cmp), "=q" (ret): "r" (set)) : "+m" (*a), "+a" (*cmp), "=q" (ret): "r" (set))
#ifdef MY_ATOMIC_MODE_DUMMY #ifdef MY_ATOMIC_MODE_DUMMY
...@@ -49,14 +57,14 @@ ...@@ -49,14 +57,14 @@
#else #else
/* /*
Actually 32-bit reads/writes are always atomic on x86 Actually 32-bit reads/writes are always atomic on x86
But we add LOCK here anyway to force memory barriers But we add LOCK_prefix here anyway to force memory barriers
*/ */
#define make_atomic_load_body(S) \ #define make_atomic_load_body(S) \
ret=0; \ ret=0; \
asm volatile (LOCK "; cmpxchg %2, %0" \ asm volatile (LOCK_prefix "; cmpxchg %2, %0" \
: "+m" (*a), "+a" (ret): "r" (ret)) : "+m" (*a), "+a" (ret): "r" (ret))
#define make_atomic_store_body(S) \ #define make_atomic_store_body(S) \
asm volatile ("; xchg %0, %1;" : "+m" (*a) : "r" (v)) asm volatile ("; xchg %0, %1;" : "+m" (*a), "+r" (v))
#endif #endif
#endif /* ATOMIC_X86_GCC_INCLUDED */ #endif /* ATOMIC_X86_GCC_INCLUDED */
This diff is collapsed.
/* Copyright (C) 2006 MySQL AB /* Copyright (C) 2006 MySQL AB, 2008-2009 Sun Microsystems, Inc
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include <my_global.h> #include <my_global.h>
#include <my_pthread.h> #include <my_sys.h>
#ifndef HAVE_INLINE #ifndef HAVE_INLINE
/* the following will cause all inline functions to be instantiated */ /* the following will cause all inline functions to be instantiated */
...@@ -43,3 +43,32 @@ int my_atomic_initialize() ...@@ -43,3 +43,32 @@ int my_atomic_initialize()
#endif #endif
} }
#ifdef SAFE_MUTEX
#undef pthread_mutex_init
#undef pthread_mutex_destroy
#undef pthread_mutex_lock
#undef pthread_mutex_unlock
void plain_pthread_mutex_init(safe_mutex_t *m)
{
pthread_mutex_init(& m->mutex, NULL);
}
void plain_pthread_mutex_destroy(safe_mutex_t *m)
{
pthread_mutex_destroy(& m->mutex);
}
void plain_pthread_mutex_lock(safe_mutex_t *m)
{
pthread_mutex_lock(& m->mutex);
}
void plain_pthread_mutex_unlock(safe_mutex_t *m)
{
pthread_mutex_unlock(& m->mutex);
}
#endif
/* Copyright (C) 2006 MySQL AB /* Copyright (C) 2006 MySQL AB, 2008-2009 Sun Microsystems, Inc
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
...@@ -16,24 +16,34 @@ ...@@ -16,24 +16,34 @@
/* get the number of (online) CPUs */ /* get the number of (online) CPUs */
#include "mysys_priv.h" #include "mysys_priv.h"
#ifdef HAVE_UNISTD_H
#include <unistd.h> #include <unistd.h>
#endif
static int ncpus=0; static int ncpus=0;
#ifdef _SC_NPROCESSORS_ONLN
int my_getncpus() int my_getncpus()
{ {
if (!ncpus) if (!ncpus)
{
#ifdef _SC_NPROCESSORS_ONLN
ncpus= sysconf(_SC_NPROCESSORS_ONLN); ncpus= sysconf(_SC_NPROCESSORS_ONLN);
return ncpus; #elif defined(__WIN__)
} SYSTEM_INFO sysinfo;
/*
* We are not calling GetNativeSystemInfo here because (1) we
* don't believe that they return different values for number
* of processors and (2) if WOW64 limits processors for Win32
* then we don't want to try to override that.
*/
GetSystemInfo(&sysinfo);
ncpus= sysinfo.dwNumberOfProcessors;
#else #else
/* unknown */ /* unknown so play safe: assume SMP and forbid uniprocessor build */
int my_getncpus() ncpus= 2;
{
return 2;
}
#endif #endif
}
return ncpus;
}
...@@ -16,6 +16,8 @@ ...@@ -16,6 +16,8 @@
AM_CPPFLAGS = @ZLIB_INCLUDES@ -I$(top_builddir)/include AM_CPPFLAGS = @ZLIB_INCLUDES@ -I$(top_builddir)/include
AM_CPPFLAGS += -I$(top_srcdir)/include -I$(top_srcdir)/unittest/mytap AM_CPPFLAGS += -I$(top_srcdir)/include -I$(top_srcdir)/unittest/mytap
noinst_HEADERS = thr_template.c
LDADD = $(top_builddir)/unittest/mytap/libmytap.a \ LDADD = $(top_builddir)/unittest/mytap/libmytap.a \
$(top_builddir)/mysys/libmysys.a \ $(top_builddir)/mysys/libmysys.a \
$(top_builddir)/dbug/libdbug.a \ $(top_builddir)/dbug/libdbug.a \
......
/* Copyright (C) 2006 MySQL AB /* Copyright (C) 2006-2008 MySQL AB, 2008 Sun Microsystems, Inc.
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
...@@ -13,10 +13,7 @@ ...@@ -13,10 +13,7 @@
along with this program; if not, write to the Free Software along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include <my_global.h> #include "thr_template.c"
#include <my_sys.h>
#include <my_atomic.h>
#include <tap.h>
/* at least gcc 3.4.5 and 3.4.6 (but not 3.2.3) on RHEL */ /* at least gcc 3.4.5 and 3.4.6 (but not 3.2.3) on RHEL */
#if __GNUC__ == 3 && __GNUC_MINOR__ == 4 #if __GNUC__ == 3 && __GNUC_MINOR__ == 4
...@@ -25,181 +22,125 @@ ...@@ -25,181 +22,125 @@
#define GCC_BUG_WORKAROUND #define GCC_BUG_WORKAROUND
#endif #endif
int32 a32,b32,c32; volatile uint32 b32;
volatile int32 c32;
my_atomic_rwlock_t rwl; my_atomic_rwlock_t rwl;
pthread_attr_t thr_attr;
pthread_mutex_t mutex;
pthread_cond_t cond;
int N;
/* add and sub a random number in a loop. Must get 0 at the end */ /* add and sub a random number in a loop. Must get 0 at the end */
pthread_handler_t test_atomic_add_handler(void *arg) pthread_handler_t test_atomic_add(void *arg)
{ {
int m=*(int *)arg; int m= (*(int *)arg)/2;
GCC_BUG_WORKAROUND int32 x; GCC_BUG_WORKAROUND int32 x;
for (x=((int)((long)(&m))); m ; m--) for (x= ((int)(intptr)(&m)); m ; m--)
{ {
x=x*m+0x87654321; x= (x*m+0x87654321) & INT_MAX32;
my_atomic_rwlock_wrlock(&rwl); my_atomic_rwlock_wrlock(&rwl);
my_atomic_add32(&a32, x); my_atomic_add32(&bad, x);
my_atomic_rwlock_wrunlock(&rwl); my_atomic_rwlock_wrunlock(&rwl);
my_atomic_rwlock_wrlock(&rwl); my_atomic_rwlock_wrlock(&rwl);
my_atomic_add32(&a32, -x); my_atomic_add32(&bad, -x);
my_atomic_rwlock_wrunlock(&rwl); my_atomic_rwlock_wrunlock(&rwl);
} }
pthread_mutex_lock(&mutex); pthread_mutex_lock(&mutex);
N--; if (!--running_threads) pthread_cond_signal(&cond);
if (!N) pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex); pthread_mutex_unlock(&mutex);
return 0; return 0;
} }
/* /*
1. generate thread number 0..N-1 from b32 1. generate thread number 0..N-1 from b32
2. add it to a32 2. add it to bad
3. swap thread numbers in c32 3. swap thread numbers in c32
4. (optionally) one more swap to avoid 0 as a result 4. (optionally) one more swap to avoid 0 as a result
5. subtract result from a32 5. subtract result from bad
must get 0 in a32 at the end must get 0 in bad at the end
*/ */
pthread_handler_t test_atomic_swap_handler(void *arg) pthread_handler_t test_atomic_fas(void *arg)
{ {
int m=*(int *)arg; int m= *(int *)arg;
int32 x; int32 x;
my_atomic_rwlock_wrlock(&rwl); my_atomic_rwlock_wrlock(&rwl);
x=my_atomic_add32(&b32, 1); x= my_atomic_add32(&b32, 1);
my_atomic_rwlock_wrunlock(&rwl); my_atomic_rwlock_wrunlock(&rwl);
my_atomic_rwlock_wrlock(&rwl); my_atomic_rwlock_wrlock(&rwl);
my_atomic_add32(&a32, x); my_atomic_add32(&bad, x);
my_atomic_rwlock_wrunlock(&rwl); my_atomic_rwlock_wrunlock(&rwl);
for (; m ; m--) for (; m ; m--)
{ {
my_atomic_rwlock_wrlock(&rwl); my_atomic_rwlock_wrlock(&rwl);
x=my_atomic_swap32(&c32, x); x= my_atomic_fas32(&c32, x);
my_atomic_rwlock_wrunlock(&rwl); my_atomic_rwlock_wrunlock(&rwl);
} }
if (!x) if (!x)
{ {
my_atomic_rwlock_wrlock(&rwl); my_atomic_rwlock_wrlock(&rwl);
x=my_atomic_swap32(&c32, x); x= my_atomic_fas32(&c32, x);
my_atomic_rwlock_wrunlock(&rwl); my_atomic_rwlock_wrunlock(&rwl);
} }
my_atomic_rwlock_wrlock(&rwl); my_atomic_rwlock_wrlock(&rwl);
my_atomic_add32(&a32, -x); my_atomic_add32(&bad, -x);
my_atomic_rwlock_wrunlock(&rwl); my_atomic_rwlock_wrunlock(&rwl);
pthread_mutex_lock(&mutex); pthread_mutex_lock(&mutex);
N--; if (!--running_threads) pthread_cond_signal(&cond);
if (!N) pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex); pthread_mutex_unlock(&mutex);
return 0; return 0;
} }
/* /*
same as test_atomic_add_handler, but my_atomic_add32 is emulated with same as test_atomic_add, but my_atomic_add32 is emulated with
(slower) my_atomic_cas32 my_atomic_cas32 - notice that the slowdown is proportional to the
number of CPUs
*/ */
pthread_handler_t test_atomic_cas_handler(void *arg) pthread_handler_t test_atomic_cas(void *arg)
{ {
int m=*(int *)arg, ok; int m= (*(int *)arg)/2, ok= 0;
GCC_BUG_WORKAROUND int32 x,y; GCC_BUG_WORKAROUND int32 x, y;
for (x=((int)((long)(&m))); m ; m--) for (x= ((int)(intptr)(&m)); m ; m--)
{ {
my_atomic_rwlock_wrlock(&rwl); my_atomic_rwlock_wrlock(&rwl);
y=my_atomic_load32(&a32); y= my_atomic_load32(&bad);
my_atomic_rwlock_wrunlock(&rwl); my_atomic_rwlock_wrunlock(&rwl);
x= (x*m+0x87654321) & INT_MAX32;
x=x*m+0x87654321;
do { do {
my_atomic_rwlock_wrlock(&rwl); my_atomic_rwlock_wrlock(&rwl);
ok=my_atomic_cas32(&a32, &y, y+x); ok= my_atomic_cas32(&bad, &y, (uint32)y+x);
my_atomic_rwlock_wrunlock(&rwl); my_atomic_rwlock_wrunlock(&rwl);
} while (!ok); } while (!ok) ;
do { do {
my_atomic_rwlock_wrlock(&rwl); my_atomic_rwlock_wrlock(&rwl);
ok=my_atomic_cas32(&a32, &y, y-x); ok= my_atomic_cas32(&bad, &y, y-x);
my_atomic_rwlock_wrunlock(&rwl); my_atomic_rwlock_wrunlock(&rwl);
} while (!ok); } while (!ok) ;
} }
pthread_mutex_lock(&mutex); pthread_mutex_lock(&mutex);
N--; if (!--running_threads) pthread_cond_signal(&cond);
if (!N) pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex); pthread_mutex_unlock(&mutex);
return 0; return 0;
} }
void test_atomic(const char *test, pthread_handler handler, int n, int m)
{
pthread_t t;
ulonglong now=my_getsystime();
a32= 0;
b32= 0;
c32= 0;
diag("Testing %s with %d threads, %d iterations... ", test, n, m);
for (N=n ; n ; n--)
{
if (pthread_create(&t, &thr_attr, handler, &m) != 0)
{
diag("Could not create thread");
a32= 1;
goto err;
}
}
pthread_mutex_lock(&mutex);
while (N)
pthread_cond_wait(&cond, &mutex);
pthread_mutex_unlock(&mutex);
now=my_getsystime()-now;
err:
ok(a32 == 0, "tested %s in %g secs", test, ((double)now)/1e7);
}
int main() void do_tests()
{ {
int err;
MY_INIT("my_atomic-t.c");
diag("N CPUs: %d", my_getncpus());
err= my_atomic_initialize();
plan(4); plan(4);
ok(err == 0, "my_atomic_initialize() returned %d", err);
pthread_attr_init(&thr_attr); bad= my_atomic_initialize();
pthread_attr_setdetachstate(&thr_attr,PTHREAD_CREATE_DETACHED); ok(!bad, "my_atomic_initialize() returned %d", bad);
pthread_mutex_init(&mutex, 0);
pthread_cond_init(&cond, 0);
my_atomic_rwlock_init(&rwl); my_atomic_rwlock_init(&rwl);
#ifdef HPUX11 b32= c32= 0;
#define CYCLES 1000 test_concurrently("my_atomic_add32", test_atomic_add, THREADS, CYCLES);
#else b32= c32= 0;
#define CYCLES 10000 test_concurrently("my_atomic_fas32", test_atomic_fas, THREADS, CYCLES);
#endif b32= c32= 0;
#define THREADS 100 test_concurrently("my_atomic_cas32", test_atomic_cas, THREADS, CYCLES);
test_atomic("my_atomic_add32", test_atomic_add_handler, THREADS, CYCLES);
test_atomic("my_atomic_swap32", test_atomic_swap_handler, THREADS, CYCLES);
test_atomic("my_atomic_cas32", test_atomic_cas_handler, THREADS, CYCLES);
/*
workaround until we know why it crashes randomly on some machine
(BUG#22320).
*/
sleep(2);
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
pthread_attr_destroy(&thr_attr);
my_atomic_rwlock_destroy(&rwl); my_atomic_rwlock_destroy(&rwl);
return exit_status();
} }
/* Copyright (C) 2006-2008 MySQL AB, 2008 Sun Microsystems, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include <my_global.h>
#include <my_sys.h>
#include <my_atomic.h>
#include <tap.h>
volatile uint32 bad;
pthread_attr_t thr_attr;
pthread_mutex_t mutex;
pthread_cond_t cond;
uint running_threads;
void do_tests();
void test_concurrently(const char *test, pthread_handler handler, int n, int m)
{
pthread_t t;
ulonglong now= my_getsystime();
bad= 0;
diag("Testing %s with %d threads, %d iterations... ", test, n, m);
for (running_threads= n ; n ; n--)
{
if (pthread_create(&t, &thr_attr, handler, &m) != 0)
{
diag("Could not create thread");
abort();
}
}
pthread_mutex_lock(&mutex);
while (running_threads)
pthread_cond_wait(&cond, &mutex);
pthread_mutex_unlock(&mutex);
now= my_getsystime()-now;
ok(!bad, "tested %s in %g secs (%d)", test, ((double)now)/1e7, bad);
}
int main(int argc __attribute__((unused)), char **argv)
{
MY_INIT("thd_template");
if (argv[1] && *argv[1])
DBUG_SET_INITIAL(argv[1]);
pthread_mutex_init(&mutex, 0);
pthread_cond_init(&cond, 0);
pthread_attr_init(&thr_attr);
pthread_attr_setdetachstate(&thr_attr,PTHREAD_CREATE_DETACHED);
#ifdef MY_ATOMIC_MODE_RWLOCKS
#if defined(HPUX11) || defined(__POWERPC__) /* showed to be very slow (scheduler-related) */
#define CYCLES 300
#else
#define CYCLES 3000
#endif
#else
#define CYCLES 3000
#endif
#define THREADS 30
diag("N CPUs: %d, atomic ops: %s", my_getncpus(), MY_ATOMIC_MODE);
do_tests();
/*
workaround until we know why it crashes randomly on some machine
(BUG#22320).
*/
sleep(2);
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
pthread_attr_destroy(&thr_attr);
my_end(0);
return exit_status();
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment