Unverified Commit f81a83ac authored by Kenton Varda's avatar Kenton Varda Committed by GitHub

Merge pull request #846 from capnproto/clocks

Add basic system clock APIs.
parents 9c7ba5f4 7910dc81
......@@ -522,6 +522,7 @@ capnp_test_SOURCES = \
src/kj/function-test.c++ \
src/kj/io-test.c++ \
src/kj/mutex-test.c++ \
src/kj/time-test.c++ \
src/kj/threadlocal-test.c++ \
src/kj/threadlocal-pthread-test.c++ \
src/kj/filesystem-test.c++ \
......
......@@ -202,6 +202,7 @@ if(BUILD_TESTING)
debug-test.c++
io-test.c++
mutex-test.c++
time-test.c++
threadlocal-test.c++
test-test.c++
std/iostream-test.c++
......
......@@ -28,7 +28,6 @@
#include <errno.h>
#include <inttypes.h>
#include <limits>
#include <chrono>
#include <pthread.h>
#include <map>
#include <sys/wait.h>
......@@ -44,14 +43,6 @@
namespace kj {
// =======================================================================================
// Timer code common to multiple implementations
TimePoint UnixEventPort::readClock() {
return origin<TimePoint>() + std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::steady_clock::now().time_since_epoch()).count() * NANOSECONDS;
}
// =======================================================================================
// Signal code common to multiple implementations
......@@ -284,7 +275,8 @@ void UnixEventPort::gotSignal(const siginfo_t& siginfo) {
// epoll FdObserver implementation
UnixEventPort::UnixEventPort()
: timerImpl(readClock()),
: clock(systemPreciseMonotonicClock()),
timerImpl(clock.now()),
epollFd(-1),
signalFd(-1),
eventFd(-1) {
......@@ -413,7 +405,7 @@ Promise<void> UnixEventPort::FdObserver::whenWriteDisconnected() {
bool UnixEventPort::wait() {
return doEpollWait(
timerImpl.timeoutToNextEvent(readClock(), MILLISECONDS, int(maxValue))
timerImpl.timeoutToNextEvent(clock.now(), MILLISECONDS, int(maxValue))
.map([](uint64_t t) -> int { return t; })
.orDefault(-1));
}
......@@ -602,7 +594,7 @@ bool UnixEventPort::doEpollWait(int timeout) {
}
}
timerImpl.advanceTo(readClock());
timerImpl.advanceTo(clock.now());
return woken;
}
......@@ -616,7 +608,8 @@ bool UnixEventPort::doEpollWait(int timeout) {
#endif
UnixEventPort::UnixEventPort()
: timerImpl(readClock()) {
: clock(systemPreciseMonotonicClock()),
timerImpl(clock.now()) {
static_assert(sizeof(threadId) >= sizeof(pthread_t),
"pthread_t is larger than a long long on your platform. Please port.");
*reinterpret_cast<pthread_t*>(&threadId) = pthread_self();
......@@ -854,7 +847,7 @@ bool UnixEventPort::wait() {
sigprocmask(SIG_UNBLOCK, &newMask, &origMask);
pollContext.run(
timerImpl.timeoutToNextEvent(readClock(), MILLISECONDS, int(maxValue))
timerImpl.timeoutToNextEvent(clock.now(), MILLISECONDS, int(maxValue))
.map([](uint64_t t) -> int { return t; })
.orDefault(-1));
......@@ -863,7 +856,7 @@ bool UnixEventPort::wait() {
// Queue events.
pollContext.processResults();
timerImpl.advanceTo(readClock());
timerImpl.advanceTo(clock.now());
return false;
}
......@@ -925,7 +918,7 @@ bool UnixEventPort::poll() {
pollContext.run(0);
pollContext.processResults();
}
timerImpl.advanceTo(readClock());
timerImpl.advanceTo(clock.now());
return woken;
}
......
......@@ -142,12 +142,12 @@ private:
class SignalPromiseAdapter;
class ChildExitPromiseAdapter;
const MonotonicClock& clock;
TimerImpl timerImpl;
SignalPromiseAdapter* signalHead = nullptr;
SignalPromiseAdapter** signalTail = &signalHead;
TimePoint readClock();
void gotSignal(const siginfo_t& siginfo);
friend class TimerPromiseAdapter;
......
......@@ -28,7 +28,7 @@
#include "async-win32.h"
#include "debug.h"
#include <atomic>
#include <chrono>
#include "time.h"
#include "refcount.h"
#include <ntsecapi.h> // NTSTATUS
#include <ntstatus.h> // STATUS_SUCCESS
......@@ -38,7 +38,8 @@
namespace kj {
Win32IocpEventPort::Win32IocpEventPort()
: iocp(newIocpHandle()), thread(openCurrentThread()), timerImpl(readClock()) {}
: clock(systemPreciseMonotonicClock()),
iocp(newIocpHandle()), thread(openCurrentThread()), timerImpl(clock.now()) {}
Win32IocpEventPort::~Win32IocpEventPort() noexcept(false) {}
......@@ -157,17 +158,12 @@ Own<Win32EventPort::SignalObserver> Win32IocpEventPort::observeSignalState(HANDL
return waitThreads.observeSignalState(handle);
}
TimePoint Win32IocpEventPort::readClock() {
return origin<TimePoint>() + std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::steady_clock::now().time_since_epoch()).count() * NANOSECONDS;
}
bool Win32IocpEventPort::wait() {
waitIocp(timerImpl.timeoutToNextEvent(readClock(), MILLISECONDS, INFINITE - 1)
waitIocp(timerImpl.timeoutToNextEvent(clock.now(), MILLISECONDS, INFINITE - 1)
.map([](uint64_t t) -> DWORD { return t; })
.orDefault(INFINITE));
timerImpl.advanceTo(readClock());
timerImpl.advanceTo(clock.now());
return receivedWake();
}
......
......@@ -209,6 +209,8 @@ private:
class IoOperationImpl;
class IoObserverImpl;
const MonotonicClock& clock;
AutoCloseHandle iocp;
AutoCloseHandle thread;
Win32WaitObjectThreadPool waitThreads;
......@@ -216,8 +218,6 @@ private:
mutable std::atomic<bool> sentWake {false};
bool isAllowApc = false;
static TimePoint readClock();
void waitIocp(DWORD timeoutMs);
// Wait on the I/O completion port for up to timeoutMs and pump events. Does not advance the
// timer; caller must do that.
......
......@@ -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;
}
......
......@@ -30,7 +30,6 @@
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <chrono>
#include "time.h"
#ifndef _WIN32
#include <sys/mman.h>
......@@ -185,8 +184,7 @@ private:
};
TimePoint readClock() {
return origin<TimePoint>() + std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::steady_clock::now().time_since_epoch()).count() * NANOSECONDS;
return systemPreciseMonotonicClock().now();
}
} // namespace
......
// Copyright (c) 2019 Cloudflare, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#if _WIN32
#define WIN32_LEAN_AND_MEAN 1 // lolz
#endif
#include "time.h"
#include "debug.h"
#include <kj/test.h>
#include <time.h>
#if _WIN32
#include <windows.h>
#include "windows-sanity.h"
#else
#include <unistd.h>
#endif
namespace kj {
namespace {
#if _WIN32
void delay(kj::Duration d) {
Sleep(d / kj::MILLISECONDS);
}
#else
void delay(kj::Duration d) {
usleep(d / kj::MICROSECONDS);
}
#endif
KJ_TEST("calendar clocks matches unix time") {
// Check that the times returned by the calendar clock are within 1s of what time() returns.
auto& coarse = systemCoarseCalendarClock();
auto& precise = systemPreciseCalendarClock();
Date p = precise.now();
Date c = coarse.now();
time_t t = time(nullptr);
int64_t pi = (p - UNIX_EPOCH) / kj::SECONDS;
int64_t ci = (c - UNIX_EPOCH) / kj::SECONDS;
KJ_EXPECT(pi >= t - 1);
KJ_EXPECT(pi <= t + 1);
KJ_EXPECT(ci >= t - 1);
KJ_EXPECT(ci <= t + 1);
}
KJ_TEST("monotonic clocks match each other") {
// Check that the monotonic clocks return comparable times.
auto& coarse = systemCoarseMonotonicClock();
auto& precise = systemPreciseMonotonicClock();
TimePoint p = precise.now();
TimePoint c = coarse.now();
// 20ms tolerance due to Windows timeslices being quite long.
KJ_EXPECT(p < c + 20 * kj::MILLISECONDS);
KJ_EXPECT(p > c - 20 * kj::MILLISECONDS);
}
KJ_TEST("all clocks advance in real time") {
Duration coarseCalDiff;
Duration preciseCalDiff;
Duration coarseMonoDiff;
Duration preciseMonoDiff;
for (uint retryCount KJ_UNUSED: kj::zeroTo(20)) {
auto& coarseCal = systemCoarseCalendarClock();
auto& preciseCal = systemPreciseCalendarClock();
auto& coarseMono = systemCoarseMonotonicClock();
auto& preciseMono = systemPreciseMonotonicClock();
Date coarseCalBefore = coarseCal.now();
Date preciseCalBefore = preciseCal.now();
TimePoint coarseMonoBefore = coarseMono.now();
TimePoint preciseMonoBefore = preciseMono.now();
Duration delayTime = 150 * kj::MILLISECONDS;
delay(delayTime);
Date coarseCalAfter = coarseCal.now();
Date preciseCalAfter = preciseCal.now();
TimePoint coarseMonoAfter = coarseMono.now();
TimePoint preciseMonoAfter = preciseMono.now();
coarseCalDiff = coarseCalAfter - coarseCalBefore;
preciseCalDiff = preciseCalAfter - preciseCalBefore;
coarseMonoDiff = coarseMonoAfter - coarseMonoBefore;
preciseMonoDiff = preciseMonoAfter - preciseMonoBefore;
// 20ms tolerance due to Windows timeslices being quite long (and Windows sleeps being only
// accurate to the timeslice).
if (coarseCalDiff > delayTime - 20 * kj::MILLISECONDS &&
coarseCalDiff < delayTime + 20 * kj::MILLISECONDS &&
preciseCalDiff > delayTime - 20 * kj::MILLISECONDS &&
preciseCalDiff < delayTime + 20 * kj::MILLISECONDS &&
coarseMonoDiff > delayTime - 20 * kj::MILLISECONDS &&
coarseMonoDiff < delayTime + 20 * kj::MILLISECONDS &&
preciseMonoDiff > delayTime - 20 * kj::MILLISECONDS &&
preciseMonoDiff < delayTime + 20 * kj::MILLISECONDS) {
// success
return;
}
}
KJ_FAIL_EXPECT("clocks seem inaccurate even after 20 tries",
coarseCalDiff / kj::MICROSECONDS, preciseCalDiff / kj::MICROSECONDS,
coarseMonoDiff / kj::MICROSECONDS, preciseMonoDiff / kj::MICROSECONDS);
}
} // namespace
} // namespace kj
......@@ -20,10 +20,22 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#if _WIN32
#define WIN32_LEAN_AND_MEAN 1 // lolz
#define WINVER 0x0600
#define _WIN32_WINNT 0x0600
#endif
#include "time.h"
#include "debug.h"
#include <set>
#if _WIN32
#include <windows.h>
#else
#include <time.h>
#endif
namespace kj {
const Clock& nullClock() {
......@@ -35,4 +47,213 @@ const Clock& nullClock() {
return NULL_CLOCK;
}
#if _WIN32
namespace {
static constexpr int64_t WIN32_EPOCH_OFFSET = 116444736000000000ull;
// Number of 100ns intervals from Jan 1, 1601 to Jan 1, 1970.
static Date toKjDate(FILETIME t) {
int64_t value = (static_cast<uint64_t>(t.dwHighDateTime) << 32) | t.dwLowDateTime;
return (value - WIN32_EPOCH_OFFSET) * (100 * kj::NANOSECONDS) + UNIX_EPOCH;
}
class Win32CoarseClock: public Clock {
public:
Date now() const override {
FILETIME ft;
GetSystemTimeAsFileTime(&ft);
return toKjDate(ft);
}
};
class Win32PreciseClock: public Clock {
typedef VOID WINAPI GetSystemTimePreciseAsFileTimeFunc(LPFILETIME);
public:
Date now() const override {
static const GetSystemTimePreciseAsFileTimeFunc* getSystemTimePreciseAsFileTimePtr =
getGetSystemTimePreciseAsFileTime();
FILETIME ft;
if (getSystemTimePreciseAsFileTimePtr == nullptr) {
// We can't use QueryPerformanceCounter() to get any more precision because we have no way
// of knowing when the calendar clock jumps. So I guess we're stuck.
GetSystemTimeAsFileTime(&ft);
} else {
getSystemTimePreciseAsFileTimePtr(&ft);
}
return toKjDate(ft);
}
private:
static GetSystemTimePreciseAsFileTimeFunc* getGetSystemTimePreciseAsFileTime() {
// Dynamically look up the function GetSystemTimePreciseAsFileTimeFunc(). This was only
// introduced as of Windows 8, so it might be missing.
return reinterpret_cast<GetSystemTimePreciseAsFileTimeFunc*>(GetProcAddress(
GetModuleHandleA("kernel32.dll"),
"GetSystemTimePreciseAsFileTime"));
}
};
class Win32CoarseMonotonicClock: public MonotonicClock {
public:
TimePoint now() const override {
return kj::origin<TimePoint>() + GetTickCount64() * kj::MILLISECONDS;
}
};
class Win32PreciseMonotonicClock: public MonotonicClock {
// Precise clock implemented using QueryPerformanceCounter().
//
// TODO(someday): Windows 10 has QueryUnbiasedInterruptTime() and
// QueryUnbiasedInterruptTimePrecise(), a new API for monotonic timing that isn't as difficult.
// Is there any benefit to dynamically checking for these and using them if available?
public:
TimePoint now() const override {
static const QpcProperties props;
LARGE_INTEGER now;
QueryPerformanceCounter(&now);
uint64_t adjusted = now.QuadPart - props.origin;
uint64_t ns = mulDiv64(adjusted, 1'000'000'000, props.frequency);
return kj::origin<TimePoint>() + ns * kj::NANOSECONDS;
}
private:
struct QpcProperties {
uint64_t origin;
// What QueryPerformanceCounter() would have returned at the time when GetTickCount64() returned
// zero. Used to ensure that the coarse and precise timers return similar values.
uint64_t frequency;
// From QueryPerformanceFrequency().
QpcProperties() {
LARGE_INTEGER now, freqLi;
uint64_t ticks = GetTickCount64();
QueryPerformanceCounter(&now);
QueryPerformanceFrequency(&freqLi);
frequency = freqLi.QuadPart;
// Convert the millisecond tick count into performance counter ticks.
uint64_t ticksAsQpc = mulDiv64(ticks, freqLi.QuadPart, 1000);
origin = now.QuadPart - ticksAsQpc;
}
};
static inline uint64_t mulDiv64(uint64_t value, uint64_t numer, uint64_t denom) {
// Inspired by:
// https://github.com/rust-lang/rust/pull/22788/files#diff-24f054cd23f65af3b574c6ce8aa5a837R54
// Computes (value*numer)/denom without overflow, as long as both
// (numer*denom) and the overall result fit into 64 bits.
uint64_t q = value / denom;
uint64_t r = value % denom;
return q * numer + r * numer / denom;
}
};
} // namespace
const Clock& systemCoarseCalendarClock() {
static constexpr Win32CoarseClock clock;
return clock;
}
const Clock& systemPreciseCalendarClock() {
static constexpr Win32PreciseClock clock;
return clock;
}
const MonotonicClock& systemCoarseMonotonicClock() {
static constexpr Win32CoarseMonotonicClock clock;
return clock;
}
const MonotonicClock& systemPreciseMonotonicClock() {
static constexpr Win32PreciseMonotonicClock clock;
return clock;
}
#else
namespace {
class PosixClock: public Clock {
public:
constexpr PosixClock(clockid_t clockId): clockId(clockId) {}
Date now() const override {
struct timespec ts;
KJ_SYSCALL(clock_gettime(clockId, &ts));
return UNIX_EPOCH + ts.tv_sec * kj::SECONDS + ts.tv_nsec * kj::NANOSECONDS;
}
private:
clockid_t clockId;
};
class PosixMonotonicClock: public MonotonicClock {
public:
constexpr PosixMonotonicClock(clockid_t clockId): clockId(clockId) {}
TimePoint now() const override {
struct timespec ts;
KJ_SYSCALL(clock_gettime(clockId, &ts));
return kj::origin<TimePoint>() + ts.tv_sec * kj::SECONDS + ts.tv_nsec * kj::NANOSECONDS;
}
private:
clockid_t clockId;
};
} // namespace
// FreeBSD has "_PRECISE", but Linux just defaults to precise.
#ifndef CLOCK_REALTIME_PRECISE
#define CLOCK_REALTIME_PRECISE CLOCK_REALTIME
#endif
#ifndef CLOCK_MONOTONIC_PRECISE
#define CLOCK_MONOTONIC_PRECISE CLOCK_MONOTONIC
#endif
// FreeBSD has "_FAST", Linux has "_COARSE".
// MacOS has an "_APPROX" but only for CLOCK_MONOTONIC_RAW, which isn't helpful.
#ifndef CLOCK_REALTIME_COARSE
#ifdef CLOCK_REALTIME_FAST
#define CLOCK_REALTIME_COARSE CLOCK_REALTIME_FAST
#else
#define CLOCK_REALTIME_COARSE CLOCK_REALTIME
#endif
#endif
#ifndef CLOCK_MONOTONIC_COARSE
#ifdef CLOCK_MONOTONIC_FAST
#define CLOCK_MONOTONIC_COARSE CLOCK_MONOTONIC_FAST
#else
#define CLOCK_MONOTONIC_COARSE CLOCK_MONOTONIC
#endif
#endif
const Clock& systemCoarseCalendarClock() {
static constexpr PosixClock clock(CLOCK_REALTIME_COARSE);
return clock;
}
const Clock& systemPreciseCalendarClock() {
static constexpr PosixClock clock(CLOCK_REALTIME_PRECISE);
return clock;
}
const MonotonicClock& systemCoarseMonotonicClock() {
static constexpr PosixMonotonicClock clock(CLOCK_MONOTONIC_COARSE);
return clock;
}
const MonotonicClock& systemPreciseMonotonicClock() {
static constexpr PosixMonotonicClock clock(CLOCK_MONOTONIC_PRECISE);
return clock;
}
#endif
} // namespace kj
......@@ -50,8 +50,9 @@ constexpr Duration HOURS = 60 * MINUTES;
constexpr Duration DAYS = 24 * HOURS;
using TimePoint = Absolute<Duration, _::TimeLabel>;
// An absolute time measured by some particular instance of `Timer`. `Time`s from two different
// `Timer`s may be measured from different origins and so are not necessarily compatible.
// An absolute time measured by some particular instance of `Timer` or `MonotonicClock`. `Time`s
// from two different `Timer`s or `MonotonicClock`s may be measured from different origins and so
// are not necessarily compatible.
using Date = Absolute<Duration, _::DateLabel>;
// A point in real-world time, measured relative to the Unix epoch (Jan 1, 1970 00:00:00 UTC).
......@@ -65,8 +66,46 @@ public:
virtual Date now() const = 0;
};
class MonotonicClock {
// Interface to read time in a way that increases as real-world time increases, independent of
// any manual changes to the calendar date/time. Such a clock never "goes backwards" even if the
// system administrator changes the calendar time or suspends the system. However, this clock's
// time points are only meaningful in comparison to other time points from the same clock, and
// cannot be used to determine the current calendar date.
public:
virtual TimePoint now() const = 0;
};
const Clock& nullClock();
// A clock which always returns UNIX_EPOCH as the current time. Useful when you don't care about
// time.
const Clock& systemCoarseCalendarClock();
const Clock& systemPreciseCalendarClock();
// A clock that reads the real system time.
//
// In well-designed code, this should only be called by the top-level dependency injector. All
// other modules should request that the caller provide a Clock so that alternate clock
// implementations can be injected for testing, simulation, reproducibility, and other purposes.
//
// The "coarse" version has precision around 1-10ms, while the "precise" version has precision
// better than 1us. The "precise" version may be slightly slower, though on modern hardware and
// a reasonable operating system the difference is usually negligible.
//
// Note: On Windows prior to Windows 8, there is no precise calendar clock; the "precise" clock
// will be no more precise than the "coarse" clock in this case.
const MonotonicClock& systemCoarseMonotonicClock();
const MonotonicClock& systemPreciseMonotonicClock();
// A MonotonicClock that reads the real system time.
//
// In well-designed code, this should only be called by the top-level dependency injector. All
// other modules should request that the caller provide a Clock so that alternate clock
// implementations can be injected for testing, simulation, reproducibility, and other purposes.
//
// The "coarse" version has precision around 1-10ms, while the "precise" version has precision
// better than 1us. The "precise" version may be slightly slower, though on modern hardware and
// a reasonable operating system the difference is usually negligible.
} // namespace kj
......@@ -31,7 +31,7 @@
namespace kj {
class Timer {
class Timer: public MonotonicClock {
// Interface to time and timer functionality.
//
// Each `Timer` may have a different origin, and some `Timer`s may in fact tick at a different
......@@ -40,7 +40,7 @@ class Timer {
// date as tracked by the system is manually modified.
public:
virtual TimePoint now() = 0;
virtual TimePoint now() const = 0;
// Returns the current value of a clock that moves steadily forward, independent of any
// changes in the wall clock. The value is updated every time the event loop waits,
// and is constant in-between waits.
......@@ -99,7 +99,7 @@ public:
// Set the time to `time` and fire any at() events that have been passed.
// implements Timer ----------------------------------------------------------
TimePoint now() override;
TimePoint now() const override;
Promise<void> atTime(TimePoint time) override;
Promise<void> afterDelay(Duration delay) override;
......@@ -127,6 +127,6 @@ Promise<T> Timer::timeoutAfter(Duration delay, Promise<T>&& promise) {
}));
}
inline TimePoint TimerImpl::now() { return time; }
inline TimePoint TimerImpl::now() const { return time; }
} // namespace kj
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