summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Gamari <ben@smart-cactus.org>2021-07-26 11:01:11 -0700
committerBen Gamari <ben@smart-cactus.org>2021-07-28 09:44:33 -0400
commit6e15e66c0dfe631152249dd669fc54976ab4c182 (patch)
tree60b13750a6fa75ff850858396d21406eda538f0b
parent10678945c1d3261273a1d7a389d14a69f4e28567 (diff)
downloadhaskell-wip/T20144.tar.gz
rts/OSThreads: Fix reference clock of timedWaitConditionwip/T20144
Previously `timedWaitCondition` assumed that timeouts were referenced against `CLOCK_MONOTONIC`. This is wrong; by default `pthread_cond_timedwait` references against `CLOCK_REALTIME`, although this can be overridden using `pthread_condattr_setclock`. Fix this and add support for using `CLOCK_MONOTONIC` whenever possible as it is more robust against system time changes and is likely cheaper to query. Unfortunately, this is complicated by the fact that older versions of Darwin did not provide `clock_gettime`, which means we also need to introduce a fallback path using `gettimeofday`. Fixes #20144.
-rw-r--r--includes/rts/OSThreads.h12
-rw-r--r--m4/fp_check_pthreads.m42
-rw-r--r--rts/posix/OSThreads.c45
3 files changed, 46 insertions, 13 deletions
diff --git a/includes/rts/OSThreads.h b/includes/rts/OSThreads.h
index 08d90de06e..d24a1313a6 100644
--- a/includes/rts/OSThreads.h
+++ b/includes/rts/OSThreads.h
@@ -27,7 +27,17 @@
#include <pthread.h>
#include <errno.h>
-typedef pthread_cond_t Condition;
+typedef struct {
+ pthread_cond_t cond;
+
+ // Which clock are pthread_cond_timedwait calls referenced against?
+ // N.B. Some older Darwin releases don't support clock_gettime. However, we
+ // do want to reference to CLOCK_MONOTONIC whenever possible as it is more
+ // robust against system time changes and is likely cheaper to query.
+#if defined(HAVE_CLOCK_GETTIME) && defined(HAVE_PTHREAD_CONDATTR_SETCLOCK)
+ clockid_t timeout_clk;
+#endif
+} Condition;
typedef pthread_mutex_t Mutex;
typedef pthread_t OSThreadId;
typedef pthread_key_t ThreadLocalKey;
diff --git a/m4/fp_check_pthreads.m4 b/m4/fp_check_pthreads.m4
index 8160f76d4b..ee5b364e93 100644
--- a/m4/fp_check_pthreads.m4
+++ b/m4/fp_check_pthreads.m4
@@ -115,4 +115,6 @@ AC_DEFUN([FP_CHECK_PTHREADS],
],
AC_MSG_RESULT(no)
)
+
+ AC_CHECK_FUNCS_ONCE([pthread_condattr_setclock])
])
diff --git a/rts/posix/OSThreads.c b/rts/posix/OSThreads.c
index 04375006d2..b5eda78648 100644
--- a/rts/posix/OSThreads.c
+++ b/rts/posix/OSThreads.c
@@ -91,6 +91,9 @@
#include <numa.h>
#endif
+// For gettimeofday()
+#include <sys/time.h>
+
// TODO does this need configure magic?
#include <time.h>
@@ -104,44 +107,62 @@
void
initCondition( Condition* pCond )
{
- CHECK(pthread_cond_init(pCond, NULL) == 0);
+ pthread_condattr_t attr;
+ CHECK(pthread_condattr_init(&attr) == 0);
+#if defined(HAVE_CLOCK_GETTIME) && defined(HAVE_PTHREAD_CONDATTR_SETCLOCK)
+ pCond->timeout_clk = CLOCK_REALTIME;
+ if (pthread_condattr_setclock(&attr, CLOCK_MONOTONIC) == 0) {
+ pCond->timeout_clk = CLOCK_MONOTONIC;
+ }
+#endif
+ CHECK(pthread_cond_init(&pCond->cond, &attr) == 0);
+ CHECK(pthread_condattr_destroy(&attr) == 0);
}
void
closeCondition( Condition* pCond )
{
- CHECK(pthread_cond_destroy(pCond) == 0);
+ CHECK(pthread_cond_destroy(&pCond->cond) == 0);
}
void
broadcastCondition ( Condition* pCond )
{
- CHECK(pthread_cond_broadcast(pCond) == 0);
+ CHECK(pthread_cond_broadcast(&pCond->cond) == 0);
}
void
signalCondition ( Condition* pCond )
{
- CHECK(pthread_cond_signal(pCond) == 0);
+ CHECK(pthread_cond_signal(&pCond->cond) == 0);
}
void
waitCondition ( Condition* pCond, Mutex* pMut )
{
- CHECK(pthread_cond_wait(pCond,pMut) == 0);
+ CHECK(pthread_cond_wait(&pCond->cond, pMut) == 0);
}
bool
timedWaitCondition ( Condition* pCond, Mutex* pMut, Time timeout) {
- timeout += getMonotonicNSec();
- uint64_t secs = TimeToSeconds(timeout);
+ struct timespec ts;
+
+#if defined(HAVE_CLOCK_GETTIME) && defined(HAVE_PTHREAD_CONDATTR_SETCLOCK)
+ CHECK(clock_gettime(pCond->timeout_clk, &ts) == 0);
+#else
+ struct timeval tv;
+ CHECK(gettimeofday(&tv, NULL) == 0);
+ ts.tv_sec = tv.tv_sec;
+ ts.tv_nsec = 1000 * tv.tv_usec;
+#endif
- const struct timespec t = (struct timespec) {
- .tv_sec = secs,
- .tv_nsec = TimeToNS(timeout - SecondsToTime(secs))
- };
+ uint64_t sec = TimeToSeconds(timeout);
+ ts.tv_sec += sec;
+ ts.tv_nsec += TimeToNS(timeout - SecondsToTime(sec));
+ ts.tv_sec += ts.tv_nsec / 1000000000;
+ ts.tv_nsec %= 1000000000;
- int ret = pthread_cond_timedwait(pCond,pMut, &t);
+ int ret = pthread_cond_timedwait(&pCond->cond, pMut, &ts);
switch (ret) {
case ETIMEDOUT:
return false;