Considering Strictly Monotonic Time
Monotonic time is a frequently used, load bearing abstraction. Monotonicity is often enforced using the following code:
fn now(clock: *Clock) Instant {
const t_raw = os_time_monotonic();
const t = @max(t_raw, clock.guard);
assert(t >= clock.guard);
assert(t >= t_raw);
clock.guard = t;
return t;
}
That is, ask the OS about the current monotonic time, but don’t trust the result too much and clamp it using an in-process guard. Under normal scenarios, you can trust the OS promise of monotonicity, but, empirically, there’s a long tail of different scenarios where the promise isn’t upheld: https://github.com/rust-lang/rust/pull/56988
Today I realized that, if you are doing the above, you might as well force the time to be strictly monotonic:
const t = @max(t_raw, clock.guard + 1ns);
assert(t > clock.guard);
The benefit of strict monotonicity is that you can tighten asserts,
assert(past <= present)
can become
assert(past < present)
and that additionally catches the bug where you pass in exactly the same instance. In other words, the <= version explicitly allows either query-ing the time
again, or using the old value directly.
Conversely, with strictly monotonic time, you know that if you see two
numerically identical time instances, they must have been ultimately
derived from the exact same call to now(). Time becomes
fundamentally less ambiguous.
The constraint here is that the resolution of the time value (not the clock resolution) needs to be high enough, to make sure
that repeated +1 don’t move you into the future, but
nanosecond precision seems fine for that.