Commit 7910dc81 authored by Kenton Varda's avatar Kenton Varda

Use KJ clocks in condvar implementation and test.

I didn't bother for posix condvars since they're kind of explicitly tied to posix clocks and it would have saved like two lines of code. But for Windows it's nice to stop using QueryPerformanceCounter() directly with the complicated math.
parent 22b88f98
......@@ -40,7 +40,6 @@
#else
#include <pthread.h>
#include <unistd.h>
#include <time.h>
#endif
namespace kj {
......@@ -48,32 +47,8 @@ namespace {
#if _WIN32
inline void delay() { Sleep(10); }
LARGE_INTEGER qpcBase;
LARGE_INTEGER qpcFreq;
bool qpcInitialized = false;
TimePoint now() {
// 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); }
TimePoint now() {
struct timespec now;
KJ_SYSCALL(clock_gettime(CLOCK_MONOTONIC, &now));
return kj::origin<TimePoint>() + now.tv_sec * kj::SECONDS + now.tv_nsec * kj::NANOSECONDS;
}
#endif
TEST(Mutex, MutexGuarded) {
......@@ -246,6 +221,7 @@ TEST(Mutex, When) {
}
TEST(Mutex, WhenWithTimeout) {
auto& clock = systemPreciseMonotonicClock();
MutexGuarded<uint> value(123);
// A timeout that won't expire.
......@@ -299,17 +275,17 @@ TEST(Mutex, WhenWithTimeout) {
}
{
auto start = now();
auto start = clock.now();
uint m = value.when([](uint n) { return n == 0; }, [&](uint& n) {
KJ_ASSERT(n == 101);
KJ_EXPECT(now() - start >= 10 * kj::MILLISECONDS);
KJ_EXPECT(clock.now() - start >= 10 * kj::MILLISECONDS);
return 12;
}, 10 * kj::MILLISECONDS);
KJ_EXPECT(m == 12);
m = value.when([](uint n) { return n == 0; }, [&](uint& n) {
KJ_ASSERT(n == 101);
KJ_EXPECT(now() - start >= 20 * kj::MILLISECONDS);
KJ_EXPECT(clock.now() - start >= 20 * kj::MILLISECONDS);
return 34;
}, 10 * kj::MILLISECONDS);
KJ_EXPECT(m == 34);
......@@ -351,9 +327,9 @@ TEST(Mutex, WhenWithTimeout) {
}, LONG_TIMEOUT);
KJ_EXPECT(m == 321);
auto start = now();
auto start = clock.now();
m = value.when([](uint n) { return n == 0; }, [&](uint& n) {
KJ_EXPECT(now() - start >= 10 * kj::MILLISECONDS);
KJ_EXPECT(clock.now() - start >= 10 * kj::MILLISECONDS);
return n + 1;
}, 10 * kj::MILLISECONDS);
KJ_EXPECT(m == 322);
......@@ -374,10 +350,12 @@ TEST(Mutex, WhenWithTimeout) {
TEST(Mutex, WhenWithTimeoutPreciseTiming) {
// Test that MutexGuarded::when() with a timeout sleeps for precisely the right amount of time.
auto& clock = systemPreciseMonotonicClock();
for (uint retryCount = 0; retryCount < 20; retryCount++) {
MutexGuarded<uint> value(123);
auto start = now();
auto start = clock.now();
uint m = value.when([&value](uint n) {
// HACK: Reset the value as a way of testing what happens when the waiting thread is woken
// up but then finds it's not ready yet.
......@@ -389,7 +367,7 @@ TEST(Mutex, WhenWithTimeoutPreciseTiming) {
KJ_EXPECT(m == 456);
auto t = now() - start;
auto t = clock.now() - start;
KJ_EXPECT(t >= 20 * kj::MILLISECONDS);
if (t <= 22 * kj::MILLISECONDS) {
return;
......@@ -402,6 +380,8 @@ TEST(Mutex, WhenWithTimeoutPreciseTimingAfterInterrupt) {
// Test that MutexGuarded::when() with a timeout sleeps for precisely the right amount of time,
// even if the thread is spuriously woken in the middle.
auto& clock = systemPreciseMonotonicClock();
for (uint retryCount = 0; retryCount < 20; retryCount++) {
MutexGuarded<uint> value(123);
......@@ -410,7 +390,7 @@ TEST(Mutex, WhenWithTimeoutPreciseTimingAfterInterrupt) {
value.lockExclusive().induceSpuriousWakeupForTest();
});
auto start = now();
auto start = clock.now();
uint m = value.when([](uint n) {
return n == 321;
}, [](uint& n) {
......@@ -419,7 +399,7 @@ TEST(Mutex, WhenWithTimeoutPreciseTimingAfterInterrupt) {
KJ_EXPECT(m == 456);
auto t = now() - start;
auto t = clock.now() - start;
KJ_EXPECT(t >= 20 * kj::MILLISECONDS, t / kj::MILLISECONDS);
if (t <= 22 * kj::MILLISECONDS) {
return;
......
......@@ -518,8 +518,8 @@ void Mutex::lockWhen(Predicate& predicate, Maybe<Duration> timeout) {
DWORD sleepMs;
// Only initialized if `timeout` is non-null.
LARGE_INTEGER frequency;
LARGE_INTEGER endTime;
const MonotonicClock* clock = nullptr;
kj::Maybe<kj::TimePoint> endTime;
KJ_IF_MAYBE(t, timeout) {
// Windows sleeps are inaccurate -- they can be longer *or shorter* than the requested amount.
......@@ -535,16 +535,8 @@ void Mutex::lockWhen(Predicate& predicate, Maybe<Duration> timeout) {
++sleepMs;
}
// Also compute the timeout absolute time in Performance Counter ticks, in case we need to
// restart the wait later.
QueryPerformanceFrequency(&frequency);
QueryPerformanceCounter(&endTime);
auto numerator = *t / kj::MILLISECONDS * frequency.QuadPart;
endTime.QuadPart += numerator / 1000;
if (numerator % 1000 > 0) {
// We guarantee we won't wake up too early.
++endTime.QuadPart;
}
clock = &systemPreciseMonotonicClock();
endTime = clock->now() + *t;
} else {
sleepMs = INFINITE;
}
......@@ -572,14 +564,13 @@ void Mutex::lockWhen(Predicate& predicate, Maybe<Duration> timeout) {
}
// Recompute sleep time.
if (timeout != nullptr) {
LARGE_INTEGER now;
QueryPerformanceCounter(&now);
if (endTime.QuadPart > now.QuadPart) {
uint64_t numerator = (endTime.QuadPart - now.QuadPart) * 1000;
sleepMs = numerator / frequency.QuadPart;
if (numerator % frequency.QuadPart > 0) {
KJ_IF_MAYBE(e, endTime) {
auto now = clock->now();
if (*e > now) {
auto sleepTime = *e - now;
sleepMs = sleepTime / kj::MILLISECONDS;
if (sleepTime % kj::MILLISECONDS > 0 * kj::SECONDS) {
// We guarantee we won't wake up too early.
++sleepMs;
}
......
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