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 { ...@@ -46,8 +46,24 @@ namespace {
#if _WIN32 #if _WIN32
inline void delay() { Sleep(10); } inline void delay() { Sleep(10); }
LARGE_INTEGER qpcBase;
LARGE_INTEGER qpcFreq;
bool qpcInitialized = false;
TimePoint now() { 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 #else
inline void delay() { usleep(10000); } inline void delay() { usleep(10000); }
......
...@@ -491,17 +491,27 @@ void Mutex::lockWhen(Predicate& predicate, Maybe<Duration> timeout) { ...@@ -491,17 +491,27 @@ void Mutex::lockWhen(Predicate& predicate, Maybe<Duration> timeout) {
addWaiter(waiter); addWaiter(waiter);
KJ_DEFER(removeWaiter(waiter)); KJ_DEFER(removeWaiter(waiter));
Maybe<TimePoint> endTime = timeout.map([](Duration d) { DWORD sleepMs;
return kj::origin<TimePoint>() + GetTickCount64() * kj::MILLISECONDS + d;
});
while (!predicate.check()) { // Only initialized if `timeout` is non-null.
DWORD millis = endTime.map([](TimePoint end) { LARGE_INTEGER frequency;
TimePoint now = kj::origin<TimePoint>() + GetTickCount64() * kj::MILLISECONDS; LARGE_INTEGER endTime;
return kj::max(end - now, 0 * kj::MILLISECONDS) / kj::MILLISECONDS;
}).orDefault(INFINITE);
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. // Normal result. Continue loop to check predicate.
} else { } else {
DWORD error = GetLastError(); DWORD error = GetLastError();
...@@ -521,6 +531,20 @@ void Mutex::lockWhen(Predicate& predicate, Maybe<Duration> timeout) { ...@@ -521,6 +531,20 @@ void Mutex::lockWhen(Predicate& predicate, Maybe<Duration> timeout) {
// exceptions just really should not write predicates that can throw. // exceptions just really should not write predicates that can throw.
kj::throwFatalException(kj::mv(**exception)); 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