Commit 551ab283 authored by Kenton Varda's avatar Kenton Varda

Replace GetTickCount64() with QueryPerformanceCounter().

It turns out GetTickCount64() is only precise to the nearest timeslice, which can be up to 16ms. The imprecision caused test failures.
parent e11034dd
......@@ -46,8 +46,24 @@ namespace {
#if _WIN32
inline void delay() { Sleep(10); }
LARGE_INTEGER qpcBase;
LARGE_INTEGER qpcFreq;
bool qpcInitialized = false;
TimePoint now() {
return kj::origin<TimePoint>() + GetTickCount64() * kj::MILLISECONDS;
// Use our own time origin so that QPC values are small and don't overflow when we multiply by
// 1000000.
if (!qpcInitialized) {
QueryPerformanceCounter(&qpcBase);
QueryPerformanceFrequency(&qpcFreq);
qpcInitialized = true;
}
LARGE_INTEGER qpc;
QueryPerformanceCounter(&qpc);
uint64_t micros = (qpc.QuadPart - qpcBase.QuadPart) * 1000000 / qpcFreq.QuadPart;
return kj::origin<TimePoint>() + micros * kj::MICROSECONDS;
}
#else
inline void delay() { usleep(10000); }
......
......@@ -491,17 +491,27 @@ void Mutex::lockWhen(Predicate& predicate, Maybe<Duration> timeout) {
addWaiter(waiter);
KJ_DEFER(removeWaiter(waiter));
Maybe<TimePoint> endTime = timeout.map([](Duration d) {
return kj::origin<TimePoint>() + GetTickCount64() * kj::MILLISECONDS + d;
});
DWORD sleepMs;
while (!predicate.check()) {
DWORD millis = endTime.map([](TimePoint end) {
TimePoint now = kj::origin<TimePoint>() + GetTickCount64() * kj::MILLISECONDS;
return kj::max(end - now, 0 * kj::MILLISECONDS) / kj::MILLISECONDS;
}).orDefault(INFINITE);
// Only initialized if `timeout` is non-null.
LARGE_INTEGER frequency;
LARGE_INTEGER endTime;
if (SleepConditionVariableSRW(&coercedCondvar(waiter.condvar), &coercedSrwLock, millis, 0)) {
KJ_IF_MAYBE(t, timeout) {
// Compute initial sleep time.
sleepMs = *t / kj::MILLISECONDS;
// Also compute the timeout absolute time in Performance Counter ticks, in case we need to
// restart the wait later.
QueryPerformanceFrequency(&frequency);
QueryPerformanceCounter(&endTime);
endTime.QuadPart += *t / kj::MILLISECONDS * frequency.QuadPart / 1000;
} else {
sleepMs = INFINITE;
}
while (!predicate.check()) {
if (SleepConditionVariableSRW(&coercedCondvar(waiter.condvar), &coercedSrwLock, sleepMs, 0)) {
// Normal result. Continue loop to check predicate.
} else {
DWORD error = GetLastError();
......@@ -521,6 +531,20 @@ void Mutex::lockWhen(Predicate& predicate, Maybe<Duration> timeout) {
// exceptions just really should not write predicates that can throw.
kj::throwFatalException(kj::mv(**exception));
}
// Recompute sleep time.
if (timeout != nullptr) {
LARGE_INTEGER now;
QueryPerformanceCounter(&now);
if (endTime.QuadPart > now.QuadPart) {
uint64_t remaining = endTime.QuadPart - now.QuadPart;
sleepMs = remaining * 1000 / frequency.QuadPart;
} else {
// Oops, already timed out.
return;
}
}
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment