From c300f2ee3aee4ced1fd1c03faed23ca91e44e678 Mon Sep 17 00:00:00 2001 From: Marius Gerbershagen Date: Sat, 27 May 2023 18:36:16 +0200 Subject: [PATCH] threads.h: use clock_monotonic for timeouts on posix if available Prevents threads from timing out too early or too late if the system clock is adjusted while waiting. --- src/aclocal.m4 | 2 +- src/configure | 11 ++++++----- src/configure.ac | 2 +- src/ecl/configpre.h | 6 ++++++ src/h/config-internal.h.in | 4 ++++ src/h/threads.h | 37 ++++++++++++++++++++++++++++--------- 6 files changed, 46 insertions(+), 16 deletions(-) diff --git a/src/aclocal.m4 b/src/aclocal.m4 index 86c853000..37e79fb1f 100644 --- a/src/aclocal.m4 +++ b/src/aclocal.m4 @@ -934,7 +934,7 @@ AC_CHECK_FUNC( [pthread_rwlock_init], [ AC_DEFINE([HAVE_POSIX_RWLOCK], [], [HAVE_POSIX_RWLOCK]) ], []) ], []) -AC_CHECK_FUNCS([pthread_mutex_timedlock]) +AC_CHECK_FUNCS([pthread_mutex_timedlock pthread_condattr_setclock]) ]) diff --git a/src/configure b/src/configure index 78beb9f64..041fdfe03 100755 --- a/src/configure +++ b/src/configure @@ -6233,12 +6233,13 @@ fi fi -for ac_func in pthread_mutex_timedlock +for ac_func in pthread_mutex_timedlock pthread_condattr_setclock do : - ac_fn_c_check_func "$LINENO" "pthread_mutex_timedlock" "ac_cv_func_pthread_mutex_timedlock" -if test "x$ac_cv_func_pthread_mutex_timedlock" = xyes; then : + as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" +if eval test \"x\$"$as_ac_var"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF -#define HAVE_PTHREAD_MUTEX_TIMEDLOCK 1 +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF fi @@ -9664,7 +9665,7 @@ done for ac_func in nanosleep alarm times select setenv putenv \ lstat mkstemp sigprocmask isatty tzset \ - gettimeofday getrusage system + gettimeofday getrusage system clock_gettime do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" diff --git a/src/configure.ac b/src/configure.ac index 515931e10..749cd5031 100644 --- a/src/configure.ac +++ b/src/configure.ac @@ -754,7 +754,7 @@ dnl !!! end autoscan AC_CHECK_FUNCS( [nanosleep alarm times select setenv putenv] \ [lstat mkstemp sigprocmask isatty tzset] \ - [gettimeofday getrusage system] ) + [gettimeofday getrusage system clock_gettime] ) AC_CHECK_FUNCS( [expf powf logf sqrtf cosf sinf tanf sinhf coshf tanhf] \ [floorf ceilf fabsf frexpf ldexpf log1p log1pf log1pl] \ diff --git a/src/ecl/configpre.h b/src/ecl/configpre.h index 0d0f66c71..7c5be730d 100644 --- a/src/ecl/configpre.h +++ b/src/ecl/configpre.h @@ -192,6 +192,9 @@ /* Define to 1 if you have the `cimagl' function. */ #undef HAVE_CIMAGL +/* Define to 1 if you have the `clock_gettime' function. */ +#undef HAVE_CLOCK_GETTIME + /* Define to 1 if you have the `clog' function. */ #undef HAVE_CLOG @@ -436,6 +439,9 @@ /* Define to 1 if you have the `powf' function. */ #undef HAVE_POWF +/* Define to 1 if you have the `pthread_condattr_setclock' function. */ +#undef HAVE_PTHREAD_CONDATTR_SETCLOCK + /* Define to 1 if you have the `pthread_mutex_timedlock' function. */ #undef HAVE_PTHREAD_MUTEX_TIMEDLOCK diff --git a/src/h/config-internal.h.in b/src/h/config-internal.h.in index 612bd2b53..f8196eca6 100644 --- a/src/h/config-internal.h.in +++ b/src/h/config-internal.h.in @@ -72,6 +72,8 @@ #endif /* gettimeofday() and sys/time.h */ #undef HAVE_GETTIMEOFDAY +/* clock_gettime() */ +#undef HAVE_CLOCK_GETTIME /* getrusage() and sys/resource.h */ #ifndef NACL #undef HAVE_GETRUSAGE @@ -133,6 +135,8 @@ #undef HAVE_POSIX_RWLOCK /* whether we have mutex lock operations with timeout */ #undef HAVE_PTHREAD_MUTEX_TIMEDLOCK +/* whether we can set the clock for timed waits on condition variables */ +#undef HAVE_PTHREAD_CONDATTR_SETCLOCK /* uname() for system identification */ #undef HAVE_UNAME #undef HAVE_UNISTD_H diff --git a/src/h/threads.h b/src/h/threads.h index 6610a75e1..a7c2eb2cb 100644 --- a/src/h/threads.h +++ b/src/h/threads.h @@ -37,6 +37,12 @@ #define ECL_MUTEX_TIMEOUT ETIMEDOUT #define ECL_MUTEX_DEADLOCK EDEADLK +#if defined(HAVE_PTHREAD_CONDATTR_SETCLOCK) && defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) +# define COND_VAR_CLOCK CLOCK_MONOTONIC +#else +# define COND_VAR_CLOCK CLOCK_REALTIME +#endif + /* MUTEX */ /* Non-recursive locks are only provided as an optimization; on @@ -83,15 +89,21 @@ ecl_mutex_lock(ecl_mutex_t *mutex) /* CONDITION VARIABLE */ static inline void -add_timeout_delta(struct timespec *ts, double seconds) +add_timeout_delta(struct timespec *ts, double seconds, clockid_t preferred_clock) { -#if defined(HAVE_GETTIMEOFDAY) +#if defined(HAVE_CLOCK_GETTIME) + clock_gettime(preferred_clock, ts); +#elif defined(HAVE_GETTIMEOFDAY) struct timeval tp; gettimeofday(&tp, NULL); /* Convert from timeval to timespec */ ts->tv_sec = tp.tv_sec; ts->tv_nsec = tp.tv_usec * 1000; +#else + ts->tv_sec = time(NULL); + ts->tv_nsec = 0; +#endif /* Add `seconds' delta */ ts->tv_sec += (time_t)floor(seconds); @@ -100,10 +112,6 @@ add_timeout_delta(struct timespec *ts, double seconds) ts->tv_nsec -= 1e9; ts->tv_sec++; } -#else - ts->tv_sec = time(NULL) + (time_t)floor(seconds); - ts->tv_nsec = 0; -#endif } static inline int @@ -111,7 +119,12 @@ ecl_mutex_timedlock(ecl_mutex_t *mutex, double seconds) { #if defined(HAVE_PTHREAD_MUTEX_TIMEDLOCK) struct timespec ts; - add_timeout_delta(&ts, seconds); + /* Ideally we would like to use CLOCK_MONOTONIC here to prevent + wrong timeouts when the system clock is adjusted while we are + waiting. However the posix spec says that pthread_mutex_timedlock + always uses CLOCK_REALTIME and does not define a way to set the + clock for mutexes. */ + add_timeout_delta(&ts, seconds, CLOCK_REALTIME); return pthread_mutex_timedlock(mutex, &ts); #else /* Not implemented, see mutex.d for alternative implementation using interrupts */ @@ -122,7 +135,13 @@ ecl_mutex_timedlock(ecl_mutex_t *mutex, double seconds) static inline void ecl_cond_var_init(ecl_cond_var_t *cv) { - pthread_cond_init(cv, NULL); + pthread_condattr_t attr; + pthread_condattr_init(&attr); +#if defined(HAVE_PTHREAD_CONDATTR_SETCLOCK) + pthread_condattr_setclock(&attr, COND_VAR_CLOCK); +#endif + pthread_cond_init(cv, &attr); + pthread_condattr_destroy(&attr); } static inline void @@ -141,7 +160,7 @@ static inline int ecl_cond_var_timedwait(ecl_cond_var_t *cv, ecl_mutex_t *mutex, double seconds) { struct timespec ts; - add_timeout_delta(&ts, seconds); + add_timeout_delta(&ts, seconds, COND_VAR_CLOCK); return pthread_cond_timedwait(cv, mutex, &ts); }