We can't use std's Instant as it's insufficiently specified. It neither guarantees "real time" nor does it guarantee measuring "uptime" (the time the OS has been awake rather than suspended), meaning that you can't actually rely on it in practice. In livesplit-core we definitely want real time rather than uptime. Various operating systems are problematic:
POSIX intends for CLOCK_MONOTONIC
to be real time, but this wasn't
correctly implemented in Linux and due to backwards compatibility
concerns they were never able to fix it properly. Thus CLOCK_MONOTONIC
means uptime on Linux whereas on other Unixes it means real time (the BSD
family). They however introduced CLOCK_BOOTTIME
in the Linux kernel
2.6.39 which measures real time. So the solution is to use this on all
operating systems that are based on the Linux kernel and fall back to
CLOCK_MONOTONIC
if the kernel is too old and the syscall fails.
macOS and iOS actually do the right thing for CLOCK_MONOTONIC
but Rust
actually doesn't use it on iOS and macOS, so we also need to use our
custom implementation for those too, but skip CLOCK_BOOTTIME
as that is
Linux specific. clock_gettime
itself however has only been available
since macOS 10.12 (Sierra) and iOS 10 which both got released in
September 2016. While there is mach_continuous_time
which does the same
thing, it got introduced in the same update and is not recommended by the
documentation, so it doesn't help with this problem.
On Windows std's Instant currently measures real time through
QueryPerformanceCounter
, but we may need to use a custom implementation
for it as well in case this ever changes.
Fuchsia is based on the new Zircon kernel. It has two functions for querying the time:
zx_clock_get: https://fuchsia.dev/fuchsia-src/reference/syscalls/clock_get zx_clock_get_monotonic: https://fuchsia.dev/fuchsia-src/reference/syscalls/clock_get_monotonic
zx_clock_get_monotonic
specifically calls out that it does not adjust during sleep
which seems to mean that it doesn't count the time the OS
is suspended. This is further evidenced by their libc implementation not
treating CLOCK_BOOTTIME
differently and a bug ticket being linked
there:
https://cs.opensource.google/fuchsia/fuchsia/+/main:zircon/third_party/ulib/musl/src/time/clock_gettime.c;l=40;drc=35e7a15cb21e16f0705560e5812b7a045d42c8a5
WASI seems to underspecify its monotonic
a bit, but says that it is defined as a clock measuring real time
, making it sound like a compliant
implementation should measure the time the OS is suspended as well.
In the web we use performance.now()
which they want to specify as being
required to keep counting during any sort of suspends (including both the
tab and the OS). The browsers currently do however not implement this
correctly at all, due to all of the same issues that we are facing here.
Spec Issue: w3c/hr-time#115
Chromium Bug Ticket: https://bugs.chromium.org/p/chromium/issues/detail?id=1206450
Chrome Implementation (the various time_*.cpp
):
https://github.com/chromium/chromium/tree/d7da0240cae77824d1eda25745c4022757499131/base/time
Firefox Bug Ticket: https://bugzilla.mozilla.org/show_bug.cgi?id=1709767
Firefox Implementation (the various TimeStamp_*.cpp
):
https://github.com/mozilla/gecko-dev/blob/08c493902519265d570250c8e7ce575c8cd6f5b5/mozglue/misc
WebKit Bug Ticket: https://bugs.webkit.org/show_bug.cgi?id=225610
WebKit Implementation: https://github.com/WebKit/WebKit/blob/79daff42b19103a15340e4005ac90facf1fc46c9/Source/WTF/wtf/CurrentTime.cpp#L268
We therefore need a custom implementation for all "Linux like operating systems" as well as macOS and iOS. The following list has to match libc (and our Cargo.toml): https://github.com/rust-lang/libc/blob/5632705fe1a7858d82609178ba96b13f98f8c2e6/src/unix/mod.rs#L1451-L1454
We however remove emscripten from this list as it's not actually based on
the Linux kernel and instead has its own implementation in JavaScript
where it actually errors out on CLOCK_BOOTTIME
:
https://github.com/emscripten-core/emscripten/blob/0321203d3614a97e4042ffa0c19ab770b1f5aa6c/src/library.js#L1419-L1426