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.
This commit is contained in:
Marius Gerbershagen 2023-05-27 18:36:16 +02:00
parent c1078cda7d
commit c300f2ee3a
6 changed files with 46 additions and 16 deletions

2
src/aclocal.m4 vendored
View file

@ -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])
])

11
src/configure vendored
View file

@ -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"

View file

@ -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] \

View file

@ -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

View file

@ -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

View file

@ -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);
}