The monotonic clock interface has this language:
/// A monotonic clock is a clock which has an unspecified initial value, and
OK, sure.
/// successive reads of the clock will produce non-decreasing values.
Is this right @Dan Gohman ? now() might overflow and wrap, which could be interpreted as "decreasing".
I think in wasip3 tests I will hard-code a "maximum reasonable duration" of, like, a day, and fail the test if a computed difference between two Instants is more than a day, under the theory that it's more likely to be a bug than a technically-possible outcome.
relatedly, wait_until has to bake in some assumption about wraparound, otherwise wait_until() when invoked on a now() that already passed would never wake
related: https://github.com/WebAssembly/wasi-testsuite/pull/120#pullrequestreview-3155404792 cc @Roman Volosatovs
POSIX seems to be similarly ambiguous about monotonic clock overflow.
I see two reasonable options:
Andy Wingo said:
The monotonic clock interface has this language:
/// A monotonic clock is a clock which has an unspecified initial value, andOK, sure.
/// successive reads of the clock will produce non-decreasing values.Is this right Dan Gohman ?
now()might overflow and wrap, which could be interpreted as "decreasing".
The monotonic clock returns a u64 of nanoseconds, so the intention here is that the host should trap if it's somehow live for the 584 or so years it would take to achieve a wraparound, so that users of the API never have to deal with this extremely rare and extremely awkward corner case.
We should document that more explicitly.
I've now filed https://github.com/WebAssembly/wasi-clocks/pull/96 to propose wording for this.
if the start value is unspecified, then overflow could happen at any time
the start value could be ((2 ** 64) - 1) - (the number of nanoseconds before your lunch break)
And then the spec says it would have to trap, and then everyone would stop using that implementation :slight_smile:
Pragmatically, wasi-libc needs to be able to implement clock_gettime(CLOCK_MONOTONIC, ...), and hosts will typically be using those same semantics in the implementation.
too bad this wasn't spec'd as a u128 nanos (or u64 seconds + u64 nanos), else we could implement the trap as "rely on the heat death of the universe"
(a subtype of "unobservable trap because the host no longer exists", which can thus be optimized away, I suppose)
Sounds like a universally applicable optimization
ok so it's UB to not make forward progress in C (infinite loops are UB), but if eventually no one can observe the forward progress that means all our programs are already UB -- about to go make the fastest compiler ever
(I could have sworn I've seen a joke post somewhere that riffs on basically that: C somehow technically admits a compiler that produces zero-byte executables for all programs as standards-compliant. some lesson about spec'ing the right thing here...)
For what it's worth, the "wall" clock does use a seconds+nanos time, so it can represent astronomical-scale times. It's just the monotonic clock, which is meant for timing durations that is limited to a u64.
If it wasn't for the compatibility and bookkeeping overhead problems I might suggest that instant should be a resource with methods like elapsed: func() -> duration and since(other: instant) -> duration and no integer repr
If someone can describe a use case that absolutely needs a single instance to live more than 10 years, convincingly, with a straight face, then maybe it would make sense to consider.
If you had a runtime that could "suspend"/snapshot instances to disk and resume them later it's at least plausible that an instance could experience years of actual run time, even if the clock also suspends (like CLOCK_MONOTONIC)
I think it would be something "exotic" (by current standards) like that rather than a host actually having a live instance for that long
I guess the most plausible use case would be a test harness that implements "time travel" to exercise date calculations or whatever
I suppose now could just be -> result<instant> instead of trapping?
To be clear, I do think it's plausible that someone could do this some day. But I think it's worthwhile to have an actual use case at hand rather than trying to guess.
In most use cases I can imagine, one would still want the ability to cope with rare-but-not-never traps. If you have any data that you really really care about, the best place for it to live is usually in a database, not a suuuuper-long-lived Wasm instance.
If we had a use case, then we could meaningfully answer the question of whether -> result<instant> would be good enough. If a single instance lives that long, is it ok if monotonic timers just start failing part-way through its lifetime? If not, then perhaps we really do need the version that works on handles.
I also think that monotonic-clock falls closer to the "compatibility" end of the spectrum along with e.g. udp-socket than the "model the spec" end like http. If you need time-traveling components maybe you don't use wasi-libc.
A similarly-pragmatic example is random/random which clearly expects to be running only in certain kinds of environments where you always have access to endless entropy.
every interface is imperfect, but some interfaces are useful. now: fn() -> result<instant> is not useful because for every not-extremely-exotic program in existence, the only sensible thing to do when you get an error is to trap, because theres no way to continue executing once the monotonic clock stopped working.
plenty of programs that wont use libc will use the monotonic-clock interface, for instance wstd uses it directly without going through wasi-libc. but i dont think a single user writing wasi programs today would be served by us imagining some extremely exotic user that might exist tomorrow and creating an overwrought clocks interface in anticipation. lets save our energy for all the other promises we made that we havent delivered yet
To clarify: I agree that this doesn't need to change. The speculation above is just feeling out the design tradeoffs in the context of a real need to clarify the interface docs.
Last updated: Dec 06 2025 at 06:05 UTC