From 261edf59a9e1d454ff3e25f89d23633dd18d5199 Mon Sep 17 00:00:00 2001 From: Alexander Boettcher Date: Wed, 8 Mar 2017 11:41:24 +0100 Subject: [PATCH] pthread_cond_timedwait: fix timeout calculation Due to rounding in the timeout calculation it may happen that the timeout stored in ms becomes 0, but actually some time (us or ns) are left to wait. With threads on various priorities (vbox) this may end up in endless loops. Fixes #2311 --- repos/libports/src/lib/pthread/thread.cc | 47 ++++++++++++++++++++---- 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/repos/libports/src/lib/pthread/thread.cc b/repos/libports/src/lib/pthread/thread.cc index 57773f415c..75f48def0e 100644 --- a/repos/libports/src/lib/pthread/thread.cc +++ b/repos/libports/src/lib/pthread/thread.cc @@ -586,9 +586,44 @@ extern "C" { } - static unsigned long timespec_to_ms(const struct timespec ts) + static uint64_t timeout_ms(struct timespec currtime, + struct timespec abstimeout) { - return (ts.tv_sec * 1000) + (ts.tv_nsec / (1000 * 1000)); + enum { S_IN_MS = 1000, S_IN_NS = 1000 * 1000 * 1000 }; + + if (currtime.tv_nsec >= S_IN_NS) { + currtime.tv_sec += currtime.tv_nsec / S_IN_NS; + currtime.tv_nsec = currtime.tv_nsec % S_IN_NS; + } + if (abstimeout.tv_nsec >= S_IN_NS) { + abstimeout.tv_sec += abstimeout.tv_nsec / S_IN_NS; + abstimeout.tv_nsec = abstimeout.tv_nsec % S_IN_NS; + } + + /* check whether absolute timeout is in the past */ + if (currtime.tv_sec > abstimeout.tv_sec) + return 0; + + uint64_t diff_ms = (abstimeout.tv_sec - currtime.tv_sec) * S_IN_MS; + uint64_t diff_ns = 0; + + if (abstimeout.tv_nsec >= currtime.tv_nsec) + diff_ns = abstimeout.tv_nsec - currtime.tv_nsec; + else { + /* check whether absolute timeout is in the past */ + if (diff_ms == 0) + return 0; + diff_ns = S_IN_NS - currtime.tv_nsec + abstimeout.tv_nsec; + diff_ms -= S_IN_MS; + } + + diff_ms += diff_ns / 1000 / 1000; + + /* if there is any diff then let the timeout be at least 1 MS */ + if (diff_ms == 0 && diff_ns != 0) + return 1; + + return diff_ms; } @@ -597,7 +632,6 @@ extern "C" { const struct timespec *__restrict abstime) { int result = 0; - Alarm::Time timeout = 0; if (!cond || !*cond) return EINVAL; @@ -615,10 +649,9 @@ extern "C" { else { struct timespec currtime; clock_gettime(CLOCK_REALTIME, &currtime); - unsigned long abstime_ms = timespec_to_ms(*abstime); - unsigned long currtime_ms = timespec_to_ms(currtime); - if (abstime_ms > currtime_ms) - timeout = abstime_ms - currtime_ms; + + Alarm::Time timeout = timeout_ms(currtime, *abstime); + try { c->signal_sem.down(timeout); } catch (Timeout_exception) {