From fccf36d9de7e42f139d28bc7280060d87ad6fc88 Mon Sep 17 00:00:00 2001 From: stevenj Date: Fri, 28 May 2010 17:41:12 -0400 Subject: [PATCH] use thread-local storage for Mersenne twister to make thread-safe (for compilers supporting __thread or __declspec(thread)) darcs-hash:20100528214112-c8de0-4ca1313eec0a24eb1bc4e0b4aea0bc0c93c75788.gz --- api/general.c | 6 +++++- api/nlopt-internal.h | 2 +- api/optimize.c | 3 +-- configure.ac | 1 + m4/ax_c_threadlocal.m4 | 39 +++++++++++++++++++++++++++++++++++++++ util/mt19937ar.c | 7 +++++-- 6 files changed, 52 insertions(+), 6 deletions(-) create mode 100644 m4/ax_c_threadlocal.m4 diff --git a/api/general.c b/api/general.c index 6ddd19b..c7f6a54 100644 --- a/api/general.c +++ b/api/general.c @@ -102,7 +102,7 @@ const char *nlopt_algorithm_name(nlopt_algorithm a) /*************************************************************************/ -int nlopt_srand_called = 0; +static THREADLOCAL int nlopt_srand_called = 0; void nlopt_srand(unsigned long seed) { nlopt_srand_called = 1; nlopt_init_genrand(seed); @@ -112,4 +112,8 @@ void nlopt_srand_time() { nlopt_srand(nlopt_time_seed()); } +void nlopt_srand_time_default() { + if (!nlopt_srand_called) nlopt_srand_time(); +} + /*************************************************************************/ diff --git a/api/nlopt-internal.h b/api/nlopt-internal.h index 67af6b1..b0f16c6 100644 --- a/api/nlopt-internal.h +++ b/api/nlopt-internal.h @@ -70,7 +70,7 @@ struct nlopt_opt_s { }; /*********************************************************************/ -extern int nlopt_srand_called; /* whether the random seed is initialized */ +void nlopt_srand_time_default(void); /* init the rand. seed only if unset */ /*********************************************************************/ /* global defaults set by deprecated API: */ diff --git a/api/optimize.c b/api/optimize.c index 49595c0..0877b01 100644 --- a/api/optimize.c +++ b/api/optimize.c @@ -170,8 +170,7 @@ static nlopt_result nlopt_optimize_(nlopt_opt opt, double *x, double *minf) *minf = HUGE_VAL; /* make sure rand generator is inited */ - if (!nlopt_srand_called) - nlopt_srand_time(); /* default is non-deterministic */ + nlopt_srand_time_default(); /* default is non-deterministic */ /* check bound constraints */ for (i = 0; i < n; ++i) diff --git a/configure.ac b/configure.ac index 800e96f..fcf4b81 100644 --- a/configure.ac +++ b/configure.ac @@ -44,6 +44,7 @@ AC_HEADER_TIME AC_CHECK_HEADERS([unistd.h getopt.h stdint.h]) AC_C_CONST AC_C_INLINE +AX_C_THREADLOCAL dnl find 32-bit unsigned integer type for random-number generator AC_CHECK_SIZEOF(unsigned int) diff --git a/m4/ax_c_threadlocal.m4 b/m4/ax_c_threadlocal.m4 new file mode 100644 index 0000000..59c0c89 --- /dev/null +++ b/m4/ax_c_threadlocal.m4 @@ -0,0 +1,39 @@ +dnl @synopsis AX_C_THREADLOCAL([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) +dnl @summary determine C keyword for threadlocal storage +dnl +dnl This macro tries to discover a C keyword to declare variables +dnl as having thread-local storage. Most commonly, this is either +dnl __thread [gcc] or __declspec(thread) [Windows]. +dnl +dnl On success, it #defines the THREADLOCAL preprocessor symbol to +dnl the appropriate keyword. You would then use it in C code as, e.g.: +dnl THREADLOCAL int myvariable; +dnl +dnl ACTION-IF-FOUND is a list of shell commands to run if an thread-local +dnl keyword is found, and ACTION-IF-NOT-FOUND is a list of commands +dnl to run it if one is not found. If ACTION-IF-FOUND is not specified, +dnl the default action does nothing. +dnl +dnl @version 2010-05-28 +dnl @license GPLWithACException +dnl @author Steven G. Johnson +AC_DEFUN([AX_C_THREADLOCAL], +[AC_CACHE_CHECK([for C thread-local keyword], acx_cv_c_threadlocal, +[acx_cv_c_threadlocal=unsupported + AC_LANG_SAVE + AC_LANG_C + for acx_kw in __thread "__declspec(thread)"; do + AC_TRY_COMPILE([], [static $acx_kw int x = 0;], + [acx_cv_c_threadlocal=$acx_kw; break]) + done + AC_LANG_RESTORE +]) + acx_kw="$acx_cv_c_threadlocal" + if test "$acx_kw" = unsupported; then acx_kw=""; fi + AC_DEFINE_UNQUOTED(THREADLOCAL, $acx_kw, [Define to C thread-local keyword, or to nothing if this is not supported in your compiler.]) + if test "$acx_cv_c_threadlocal" = unsupported; then + m4_default([$2],:) + else + m4_default([$1],:) + fi +]) diff --git a/util/mt19937ar.c b/util/mt19937ar.c index 5485129..f6d7f60 100644 --- a/util/mt19937ar.c +++ b/util/mt19937ar.c @@ -70,8 +70,11 @@ #define UPPER_MASK 0x80000000UL /* most significant w-r bits */ #define LOWER_MASK 0x7fffffffUL /* least significant r bits */ -static uint32_t mt[N]; /* the array for the state vector */ -static int mti=N+1; /* mti==N+1 means mt[N] is not initialized */ +/* SGJ 2010: make RNG thread-safe by declaring the RNG state as thread-local + storage, at least for GCC, MSVC, and Intel C++ */ + +static THREADLOCAL uint32_t mt[N]; /* the array for the state vector */ +static THREADLOCAL int mti=N+1; /* mti==N+1 means mt[N] is not initialized */ /* initializes mt[N] with a seed */ void nlopt_init_genrand(unsigned long s) -- 2.30.2