diff options
author | Ben Gamari <ben@smart-cactus.org> | 2021-07-26 11:01:11 -0700 |
---|---|---|
committer | Marge Bot <ben+marge-bot@smart-cactus.org> | 2021-08-02 04:14:25 -0400 |
commit | f454c0ea2e7de32786635a987885706fcd7cb01a (patch) | |
tree | efa5034ef77674b767b8d33401f5373e908421f5 /rts/posix | |
parent | 2e0f4ca128d17a7161fa41ee9e82315a1cddffb7 (diff) | |
download | haskell-f454c0ea2e7de32786635a987885706fcd7cb01a.tar.gz |
rts/OSThreads: Fix reference clock of timedWaitCondition
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.
Diffstat (limited to 'rts/posix')
-rw-r--r-- | rts/posix/OSThreads.c | 45 |
1 files changed, 33 insertions, 12 deletions
diff --git a/rts/posix/OSThreads.c b/rts/posix/OSThreads.c index 647b4840d3..980080731f 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; |