From 592bf5d9862783d712f906e6014515abae90c64a Mon Sep 17 00:00:00 2001 From: vlefevre Date: Sun, 27 Jan 2019 18:30:16 +0000 Subject: Shared cache: cleanup and various improvements / corrections. * acinclude.m4: - test $enable_shared_cache instead of $mpfr_want_shared_cache; - check ISO C11 thread support and/or POSIX thread support only when necessary; - when checking support for POSIX threads (pthread), also check that pthread_rwlock_t is supported, as it is needed by MPFR and conditionally defined in glibc's bits/pthreadtypes.h (via ); - with POSIX threads, also set CC="$PTHREAD_CC" as documented by ax_pthread (autoconf-archive). This is not guaranteed to work, but according to the ax_pthread.m4 source, in the cases where "$PTHREAD_CC" != "$CC", not setting it will probably not work either; - handle --enable-shared-cache early in MPFR_CONFIGS, because the use of POSIX threads (pthread) may need to change CC, CFLAGS, and LIBS (thus affecting other tests); - removed the now useless MPFR_CHECK_SHARED_CACHE function. * configure.ac: no longer set the mpfr_want_shared_cache variable, as enable_shared_cache (now used) already has the same usage. * acinclude.m4, configure.ac: moved the compatibility test of the configure options even earlier, from acinclude.m4 to configure.ac, just after the code that defines them. Also added an associated AC_MSG_CHECKING message for better clarity. * src/mpfr-impl.h: added a comment about the cache-related types, which depend on the locking methods. * src/mpfr-thread.h: fixed the lock macros: - in case of failure, one must abort, otherwise this would generally be undefined behavior; - added missing "do {} while (0)" (currently not mandatory). * tests/tversion.c: update concerning the shared cache, to be consistent with the other mpfr_buildopt_*_p features: - check that mpfr_buildopt_sharedcache_p() and MPFR_WANT_SHARED_CACHE match; - for the output of the value, test mpfr_buildopt_sharedcache_p() instead of the macro. * NEWS: update. (merged changesets r13032,13390-13396,13410,13412 from the trunk) git-svn-id: svn://scm.gforge.inria.fr/svn/mpfr/branches/4.0@13416 280ebfd0-de03-0410-8827-d642c229c3f4 --- NEWS | 1 + acinclude.m4 | 117 ++++++++++++++++++++++++++++-------------------------- configure.ac | 26 +++++++++++- src/mpfr-impl.h | 4 ++ src/mpfr-thread.h | 81 ++++++++++++------------------------- tests/tversion.c | 22 +++++++--- 6 files changed, 132 insertions(+), 119 deletions(-) diff --git a/NEWS b/NEWS index e2d78b9a7..e4f334270 100644 --- a/NEWS +++ b/NEWS @@ -22,6 +22,7 @@ https://www.gnu.org/licenses/ or write to the Free Software Foundation, Inc., Changes from version 4.0.1 to version 4.0.2: - Corrected minimal GMP version in the INSTALL file and the MPFR manual. +- Shared caches: cleanup; really detect lock failures (abort in this case). - Improved MPFR manual. In particular, corrected/completed the mpfr_get_str description in order to follow the historical behavior and GMP's mpf_get_str function. diff --git a/acinclude.m4 b/acinclude.m4 index 5cc1df4c6..02a11973c 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -44,6 +44,66 @@ AC_REQUIRE([MPFR_CHECK_LIBQUADMATH]) AC_REQUIRE([AC_HEADER_TIME]) AC_REQUIRE([AC_CANONICAL_HOST]) +dnl Features for the MPFR shared cache. This needs to be done +dnl quite early since this may change CC, CFLAGS and LIBS, which +dnl may affect the other tests. + +if test "$enable_shared_cache" = yes; then + +dnl Prefer ISO C11 threads (as in mpfr-thread.h). + MPFR_CHECK_C11_THREAD() + + if test "$mpfr_c11_thread_ok" != yes; then +dnl Check for POSIX threads. Since the AX_PTHREAD macro is not standard +dnl (it is provided by autoconf-archive), we need to detect whether it +dnl is left unexpanded, otherwise the configure script won't fail and +dnl "make distcheck" won't give any error, yielding buggy tarballs! +dnl The \b is necessary to avoid an error with recent ax_pthread.m4 +dnl (such as with Debian's autoconf-archive 20160320-1), which contains +dnl AX_PTHREAD_ZOS_MISSING, etc. It is not documented, but see: +dnl https://lists.gnu.org/archive/html/autoconf/2015-03/msg00011.html +dnl +dnl Note: each time a change is done in m4_pattern_forbid, autogen.sh +dnl should be tested with and without ax_pthread.m4 availability (in +dnl the latter case, there should be an error). + m4_pattern_forbid([AX_PTHREAD\b]) + AX_PTHREAD([]) + if test "$ax_pthread_ok" = yes; then + CC="$PTHREAD_CC" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + LIBS="$LIBS $PTHREAD_LIBS" +dnl Do a compilation test, as this is currently not done by AX_PTHREAD. +dnl Moreover, MPFR needs pthread_rwlock_t, which is conditionally defined +dnl in glibc's bits/pthreadtypes.h (via ), not sure why... + AC_MSG_CHECKING([for pthread_rwlock_t]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#include +]], [[ +pthread_rwlock_t lock; (void) lock; +]])], + [AC_MSG_RESULT([yes]) + mpfr_pthread_ok=yes], + [AC_MSG_RESULT([no]) + mpfr_pthread_ok=no]) + else + mpfr_pthread_ok=no + fi + fi + + AC_MSG_CHECKING(if shared cache can be supported) + if test "$mpfr_c11_thread_ok" = yes; then + AC_MSG_RESULT([yes, with ISO C11 threads]) + elif test "$mpfr_pthread_ok" = yes; then + AC_MSG_RESULT([yes, with pthread]) + else + AC_MSG_RESULT(no) + AC_MSG_ERROR([shared cache needs C11 threads or pthread support]) + fi + +fi + +dnl End of features for the MPFR shared cache. + AC_CHECK_HEADER([limits.h],, AC_MSG_ERROR([limits.h not found])) AC_CHECK_HEADER([float.h],, AC_MSG_ERROR([float.h not found])) AC_CHECK_HEADER([string.h],, AC_MSG_ERROR([string.h not found])) @@ -206,24 +266,6 @@ fi dnl Check for attribute constructor and destructor MPFR_CHECK_CONSTRUCTOR_ATTR() -dnl Check for POSIX Thread. Since the AX_PTHREAD macro is not standard -dnl (it is provided by autoconf-archive), we need to detect whether it -dnl is left unexpanded, otherwise the configure script won't fail and -dnl "make distcheck" won't give any error, yielding buggy tarballs! -dnl The \b is necessary to avoid an error with recent ax_pthread.m4 -dnl (such as with Debian's autoconf-archive 20160320-1), which contains -dnl AX_PTHREAD_ZOS_MISSING, etc. It is not documented, but see: -dnl https://lists.gnu.org/archive/html/autoconf/2015-03/msg00011.html -dnl -dnl Note: each time a change is done in m4_pattern_forbid, autogen.sh -dnl should be tested with and without ax_pthread.m4 availability (in -dnl the latter case, there should be an error). -m4_pattern_forbid([AX_PTHREAD\b]) -AX_PTHREAD([]) - -dnl Check for ISO C11 Thread -MPFR_CHECK_C11_THREAD() - dnl Check for fesetround AC_CACHE_CHECK([for fesetround], mpfr_cv_have_fesetround, [ saved_LIBS="$LIBS" @@ -504,14 +546,6 @@ LIBS="$saved_LIBS" dnl Now try to check the long double format MPFR_C_LONG_DOUBLE_FORMAT -if test "$enable_logging" = yes; then - if test "$enable_thread_safe" = yes; then - AC_MSG_ERROR([enable either `Logging' or `thread-safe', not both]) - else - enable_thread_safe=no - fi -fi - dnl Check if thread-local variables are supported. dnl At least two problems can occur in practice: dnl 1. The compilation fails, e.g. because the compiler doesn't know @@ -690,11 +724,6 @@ if test "$enable_lto" = "yes" ; then MPFR_LTO fi -dnl Check if the shared cache was requested and its requirements are ok. -if test "$mpfr_want_shared_cache" = yes ;then - MPFR_CHECK_SHARED_CACHE() -fi - ]) dnl end of MPFR_CONFIGS @@ -1517,32 +1546,6 @@ mpfr_compile_and_link() rm -f conftest* ]) -dnl MPFR_CHECK_SHARED_CACHE -dnl ---------------------- -dnl Check if the conditions for the shared cache are met: -dnl * either pthread / C11 are available. -dnl * either constructor or once. -AC_DEFUN([MPFR_CHECK_SHARED_CACHE], [ - AC_MSG_CHECKING(if shared cache is supported) - if test "$enable_logging" = yes ; then - AC_MSG_RESULT(no) - AC_MSG_ERROR([shared cache does not work with logging support.]) -dnl because logging support disables threading support - elif test "$enable_thread_safe" != yes ; then - AC_MSG_RESULT(no) - AC_MSG_ERROR([shared cache needs thread attribute.]) - elif test "$ax_pthread_ok" != yes && "$mpfr_c11_thread_ok" != yes ; then - AC_MSG_RESULT(no) - AC_MSG_ERROR([shared cache needs pthread/C11 library.]) - else - AC_MSG_RESULT(yes) - if test "$ax_pthread_ok" = yes ; then - CFLAGS="$CFLAGS $PTHREAD_CFLAGS" - LIBS="$LIBS $PTHREAD_LIBS" - fi - fi -]) - dnl MPFR_CHECK_CONSTRUCTOR_ATTR dnl --------------------------- dnl Check for constructor/destructor attributes to function. diff --git a/configure.ac b/configure.ac index 2f8ae8fd3..19f83b85d 100644 --- a/configure.ac +++ b/configure.ac @@ -167,7 +167,7 @@ AC_ARG_ENABLE(shared-cache, for all MPFR constants. It usually makes MPFR dependent on PTHREAD [[default=no]]], [ case $enableval in - yes) mpfr_want_shared_cache=yes + yes) AC_DEFINE([MPFR_WANT_SHARED_CACHE],1,[Want shared cache]) ;; no) ;; *) AC_MSG_ERROR([bad value for --enable-shared-cache: yes or no]) ;; @@ -204,6 +204,30 @@ AC_ARG_ENABLE(tune-for-coverage, *) AC_MSG_ERROR([bad value for --enable-tune-for-coverage]) ;; esac]) +dnl First, detect incompatibilities between the above configure options. +AC_MSG_CHECKING([whether configure options are compatible]) +if test "$enable_logging" = yes; then + if test "$enable_thread_safe" = yes; then + AC_MSG_RESULT([no]) + AC_MSG_ERROR([enable either logging or thread-safe, not both]) + fi +dnl The following test is done only to output a specific error message, +dnl as there would otherwise be an error due to enable_thread_safe=no. + if test "$enable_shared_cache" = yes; then + AC_MSG_RESULT([no]) + AC_MSG_ERROR([shared cache does not work with logging support]) + fi + enable_thread_safe=no +fi +if test "$enable_shared_cache" = yes; then + if test "$enable_thread_safe" = no; then + AC_MSG_RESULT([no]) + AC_MSG_ERROR([shared cache needs thread-safe support]) + fi + enable_thread_safe=yes +fi +AC_MSG_RESULT([yes]) + dnl dnl Setup CC and CFLAGS diff --git a/src/mpfr-impl.h b/src/mpfr-impl.h index d2843ab68..805e3cb4a 100644 --- a/src/mpfr-impl.h +++ b/src/mpfr-impl.h @@ -213,6 +213,10 @@ extern "C" { # define MPFR_CACHE_ATTR MPFR_THREAD_ATTR #endif +/* Note: The following structure and types depend on the MPFR build options + (including compiler options), due to the various locking methods affecting + MPFR_DEFERRED_INIT_SLAVE_DECL and MPFR_LOCK_DECL. But since this is only + internal, that's OK. */ struct __gmpfr_cache_s { mpfr_t x; int inexact; diff --git a/src/mpfr-thread.h b/src/mpfr-thread.h index ed04d9bcf..6ae66745a 100644 --- a/src/mpfr-thread.h +++ b/src/mpfr-thread.h @@ -64,38 +64,21 @@ https://www.gnu.org/licenses/ or write to the Free Software Foundation, Inc., #define MPFR_LOCK_DECL(_lock) \ mtx_t _lock; -#define MPFR_LOCK_INIT(_lock) do { \ - int error_code = mtx_init(&(_lock), mtx_plain); \ - MPFR_ASSERTD( error_code == thrd_success); \ +#define MPFR_LOCK_C(E) \ + do { \ + if ((E) != thrd_success) \ + abort (); \ } while (0) -#define MPFR_LOCK_CLEAR(_lock) do { \ - mtx_destroy(&(_lock)); \ - } while (0) - -#define MPFR_LOCK_READ(_lock) do { \ - int error_code = mtx_lock(&(_lock)); \ - MPFR_ASSERTD(error_code == thrd_success); \ - } while (0) - -#define MPFR_UNLOCK_READ(_lock) do { \ - int error_code = mtx_unlock(&(_lock)); \ - MPFR_ASSERTD(error_code == thrd_success); \ - } while (0) +#define MPFR_LOCK_INIT(_lock) MPFR_LOCK_C(mtx_init(&(_lock), mtx_plain)) +#define MPFR_LOCK_CLEAR(_lock) do { mtx_destroy(&(_lock)); } while (0) +#define MPFR_LOCK_READ(_lock) MPFR_LOCK_C(mtx_lock(&(_lock))) +#define MPFR_UNLOCK_READ(_lock) MPFR_LOCK_C(mtx_unlock(&(_lock))) +#define MPFR_LOCK_WRITE(_lock) MPFR_LOCK_C(mtx_lock(&(_lock))) +#define MPFR_UNLOCK_WRITE(_lock) MPFR_LOCK_C(mtx_unlock(&(_lock))) -#define MPFR_LOCK_WRITE(_lock) do { \ - int error_code = mtx_lock(&(_lock)); \ - MPFR_ASSERTD(error_code == thrd_success); \ - } while (0) - -#define MPFR_UNLOCK_WRITE(_lock) do { \ - int error_code = mtx_unlock(&(_lock)); \ - MPFR_ASSERTD(error_code == thrd_success); \ - } while (0) - -#define MPFR_LOCK_READ2WRITE(_lock) - -#define MPFR_LOCK_WRITE2READ(_lock) +#define MPFR_LOCK_READ2WRITE(_lock) do {} while (0) +#define MPFR_LOCK_WRITE2READ(_lock) do {} while (0) #define MPFR_ONCE_DECL(_once) \ once_flag _once; @@ -121,37 +104,25 @@ https://www.gnu.org/licenses/ or write to the Free Software Foundation, Inc., #include -#define MPFR_LOCK_DECL(_lock) \ +#define MPFR_LOCK_DECL(_lock) \ pthread_rwlock_t _lock; -#define MPFR_LOCK_INIT(_lock) do { \ - int error_code = pthread_rwlock_init(&(_lock), NULL); \ - MPFR_ASSERTD( error_code == 0); \ +#define MPFR_LOCK_C(E) \ + do { \ + if ((E) != 0) \ + abort (); \ } while (0) +#define MPFR_LOCK_INIT(_lock) MPFR_LOCK_C(pthread_rwlock_init(&(_lock), NULL)) + #define MPFR_LOCK_CLEAR(_lock) do { \ pthread_rwlock_destroy(&(_lock)); \ } while (0) -#define MPFR_LOCK_READ(_lock) do { \ - int error_code = pthread_rwlock_rdlock(&(_lock)); \ - MPFR_ASSERTD(error_code == 0); \ - } while (0) - -#define MPFR_UNLOCK_READ(_lock) do { \ - int error_code = pthread_rwlock_unlock(&(_lock)); \ - MPFR_ASSERTD(error_code == 0); \ - } while (0) - -#define MPFR_LOCK_WRITE(_lock) do { \ - int error_code = pthread_rwlock_wrlock(&(_lock)); \ - MPFR_ASSERTD(error_code == 0); \ - } while (0) - -#define MPFR_UNLOCK_WRITE(_lock) do { \ - int error_code = pthread_rwlock_unlock(&(_lock)); \ - MPFR_ASSERTD(error_code == 0); \ - } while (0) +#define MPFR_LOCK_READ(_lock) MPFR_LOCK_C(pthread_rwlock_rdlock(&(_lock))) +#define MPFR_UNLOCK_READ(_lock) MPFR_LOCK_C(pthread_rwlock_unlock(&(_lock))) +#define MPFR_LOCK_WRITE(_lock) MPFR_LOCK_C(pthread_rwlock_wrlock(&(_lock))) +#define MPFR_UNLOCK_WRITE(_lock) MPFR_LOCK_C(pthread_rwlock_unlock(&(_lock))) #define MPFR_LOCK_READ2WRITE(_lock) do { \ MPFR_UNLOCK_READ(_lock); \ @@ -168,10 +139,8 @@ https://www.gnu.org/licenses/ or write to the Free Software Foundation, Inc., #define MPFR_ONCE_INIT_VALUE PTHREAD_ONCE_INIT -#define MPFR_ONCE_CALL(_once, _func) do { \ - int error_code = pthread_once (&(_once), (_func)); \ - MPFR_ASSERTD (error_code == 0); \ - } while (0) +#define MPFR_ONCE_CALL(_once, _func) \ + MPFR_LOCK_C(pthread_once (&(_once), (_func))) #define MPFR_NEED_DEFERRED_INIT 1 diff --git a/tests/tversion.c b/tests/tversion.c index 902aed6f8..bffa8d37c 100644 --- a/tests/tversion.c +++ b/tests/tversion.c @@ -268,6 +268,17 @@ main (void) err = 1; } + if ( +#ifdef MPFR_WANT_SHARED_CACHE + ! +#endif + mpfr_buildopt_sharedcache_p ()) + { + printf ("ERROR! mpfr_buildopt_sharedcache_p() and macros" + " do not match!\n"); + err = 1; + } + (printf) ("[tversion] TLS = %s, float128 = %s, decimal = %s," " GMP internals = %s\n", mpfr_buildopt_tls_p () ? "yes" : "no", @@ -281,13 +292,14 @@ main (void) ")" : "no", mpfr_buildopt_gmpinternals_p () ? "yes" : "no"); - (puts) ("[tversion] Shared cache = " -#if defined(MPFR_WANT_SHARED_CACHE) - "yes (" MPFR_THREAD_LOCK_METHOD ")" +#ifdef MPFR_THREAD_LOCK_METHOD +# define LOCK_METHOD " (lock method: " MPFR_THREAD_LOCK_METHOD ")" #else - "no" +# define LOCK_METHOD "" #endif - ); + + (printf) ("[tversion] Shared cache = %s\n", + mpfr_buildopt_sharedcache_p () ? "yes" LOCK_METHOD : "no"); (puts) ("[tversion] intmax_t = " #if defined(_MPFR_H_HAVE_INTMAX_T) -- cgit v1.2.1