diff options
Diffstat (limited to 'third_party/rust/zeitstempel/src')
-rw-r--r-- | third_party/rust/zeitstempel/src/fallback.rs | 31 | ||||
-rw-r--r-- | third_party/rust/zeitstempel/src/lib.rs | 105 | ||||
-rw-r--r-- | third_party/rust/zeitstempel/src/linux.rs | 24 | ||||
-rw-r--r-- | third_party/rust/zeitstempel/src/mac.rs | 17 | ||||
-rw-r--r-- | third_party/rust/zeitstempel/src/win.rs | 42 |
5 files changed, 219 insertions, 0 deletions
diff --git a/third_party/rust/zeitstempel/src/fallback.rs b/third_party/rust/zeitstempel/src/fallback.rs new file mode 100644 index 0000000000..157f4c1a77 --- /dev/null +++ b/third_party/rust/zeitstempel/src/fallback.rs @@ -0,0 +1,31 @@ +use std::convert::TryInto; +use std::time::Instant; + +use once_cell::sync::Lazy; + +static INIT_TIME: Lazy<Instant> = Lazy::new(Instant::now); + +pub fn now_including_suspend() -> u64 { + // For Windows: + // Instead of relying on figuring out the underlying functions, + // we can rely on the fact that `Instant::now` maps to [QueryPerformanceCounter] on Windows, + // so by comparing it to another arbitrary timestamp we will get a duration that will include + // suspend time. + // If we use that as a timestamp we can compare later timestamps to it and that will also + // include suspend time. + // + // [QueryPerformanceCounter]: https://docs.microsoft.com/en-us/windows/win32/api/profileapi/nf-profileapi-queryperformancecounter + // + // For other operating systems: + // This fallback is not used on Linux, where it maps to `CLOCK_MONOTONIC`, which does NOT + // include suspend time. But we don't use it there, so no problem. + // + // This fallback is not used on macOS, where it maps to `mach_absolute_time`, which does NOT + // include suspend time. But we don't use it there, so no problem. + // + // For other operating systems we make no guarantees, other than that we won't panic. + let now = Instant::now(); + now.checked_duration_since(*INIT_TIME) + .and_then(|diff| diff.as_nanos().try_into().ok()) + .unwrap_or(0) +} diff --git a/third_party/rust/zeitstempel/src/lib.rs b/third_party/rust/zeitstempel/src/lib.rs new file mode 100644 index 0000000000..61736f19fa --- /dev/null +++ b/third_party/rust/zeitstempel/src/lib.rs @@ -0,0 +1,105 @@ +//! zeitstempel is German for "timestamp". +//! +//! --- +//! +//! Time's hard. Correct time is near impossible. +//! +//! This crate has one purpose: give me a timestamp as an integer, coming from a monotonic clock +//! source, include time across suspend/hibernation of the host machine and let me compare it to +//! other timestamps. +//! +//! It becomes the developer's responsibility to only compare timestamps obtained from this clock source. +//! Timestamps are not comparable across operating system reboots. +//! +//! # Why not `std::time::Instant`? +//! +//! [`std::time::Instant`] fulfills some of our requirements: +//! +//! * It's monotonic, guaranteed ([sort of][rustsource]). +//! * It can be compared to other timespans. +//! +//! However: +//! +//! * It can't be serialized. +//! * It's not guaranteed that the clock source it uses contains suspend/hibernation time across all operating systems. +//! +//! [rustsource]: https://doc.rust-lang.org/1.47.0/src/std/time.rs.html#213-237 +//! +//! # Example +//! +//! ``` +//! # use std::{thread, time::Duration}; +//! let start = zeitstempel::now(); +//! thread::sleep(Duration::from_millis(2)); +//! +//! let diff = Duration::from_nanos(zeitstempel::now() - start); +//! assert!(diff >= Duration::from_millis(2)); +//! ``` +//! +//! # Supported operating systems +//! +//! We support the following operating systems: +//! +//! * Windows\* +//! * macOS +//! * Linux +//! * Android +//! * iOS +//! +//! For other operating systems there's a fallback to `std::time::Instant`, +//! compared against a process-global fixed reference point. +//! We don't guarantee that measured time includes time the system spends in sleep or hibernation. +//! +//! \* To use native Windows 10 functionality enable the `win10plus` feature. Otherwise it will use the fallback. + +#![deny(missing_docs)] +#![deny(broken_intra_doc_links)] + +cfg_if::cfg_if! { + if #[cfg(any(target_os = "macos", target_os = "ios"))] { + mod mac; + use mac as sys; + } else if #[cfg(any(target_os = "linux", target_os = "android"))] { + mod linux; + use linux as sys; + } else if #[cfg(all(windows, feature = "win10plus"))] { + mod win; + use win as sys; + } else { + mod fallback; + use fallback as sys; + } +} + +/// Returns a timestamp corresponding to "now". +/// +/// It can be compared to other timestamps gathered from this API, as long as the host was not +/// rebooted inbetween. +/// +/// +/// ## Note +/// +/// * The difference between two timestamps will include time the system was in sleep or +/// hibernation. +/// * The difference between two timestamps gathered from this is in nanoseconds. +/// * The clocks on some operating systems, e.g. on Windows, are not nanosecond-precise. +/// The value will still use nanosecond resolution. +pub fn now() -> u64 { + sys::now_including_suspend() +} + +#[cfg(test)] +mod test { + use super::*; + use std::thread; + use std::time::Duration; + + #[test] + fn order() { + let ts1 = now(); + thread::sleep(Duration::from_millis(2)); + let ts2 = now(); + + assert!(ts1 < ts2); + } +} diff --git a/third_party/rust/zeitstempel/src/linux.rs b/third_party/rust/zeitstempel/src/linux.rs new file mode 100644 index 0000000000..e8fb8500ca --- /dev/null +++ b/third_party/rust/zeitstempel/src/linux.rs @@ -0,0 +1,24 @@ +const NS_PER_S: u64 = 1_000_000_000; + +fn timespec_to_ns(ts: libc::timespec) -> u64 { + (ts.tv_sec as u64) * NS_PER_S + (ts.tv_nsec as u64) +} + +/// The time from a clock that cannot be set +/// and represents monotonic time since some unspecified starting point, +/// that also includes any time that the system is suspended. +/// +/// See [`clock_gettime`]. +/// +/// [`clock_gettime`]: https://manpages.debian.org/buster/manpages-dev/clock_gettime.3.en.html +pub fn now_including_suspend() -> u64 { + let mut ts = libc::timespec { + tv_sec: 0, + tv_nsec: 0, + }; + unsafe { + libc::clock_gettime(libc::CLOCK_BOOTTIME, &mut ts); + } + + timespec_to_ns(ts) +} diff --git a/third_party/rust/zeitstempel/src/mac.rs b/third_party/rust/zeitstempel/src/mac.rs new file mode 100644 index 0000000000..701daa1696 --- /dev/null +++ b/third_party/rust/zeitstempel/src/mac.rs @@ -0,0 +1,17 @@ +use libc::clockid_t; + +extern "C" { + fn clock_gettime_nsec_np(clock_id: clockid_t) -> u64; +} + +const CLOCK_MONOTONIC_RAW: clockid_t = 4; + +/// The time from a clock that increments monotonically, +/// tracking the time since an arbitrary point. +/// +/// See [`clock_gettime_nsec_np`]. +/// +/// [`clock_gettime_nsec_np`]: https://opensource.apple.com/source/Libc/Libc-1158.1.2/gen/clock_gettime.3.auto.html +pub fn now_including_suspend() -> u64 { + unsafe { clock_gettime_nsec_np(CLOCK_MONOTONIC_RAW) } +} diff --git a/third_party/rust/zeitstempel/src/win.rs b/third_party/rust/zeitstempel/src/win.rs new file mode 100644 index 0000000000..37e8553498 --- /dev/null +++ b/third_party/rust/zeitstempel/src/win.rs @@ -0,0 +1,42 @@ +//! Timestamp implementation for Windows 10+ or Windows Server 2016+. +//! +//! Lower versions don't have the necessary API and should use the fallback. + +#![cfg(feature = "win10plus")] + +/// [PULONGLONG] is a pointer to [ULONGLONG], a 64-bit unsigned integer. +/// +/// [PULONGLONG]: https://docs.microsoft.com/en-us/windows/win32/winprog/windows-data-types#PULONGLONG +/// [ULONGLONG]: https://docs.microsoft.com/en-us/windows/win32/winprog/windows-data-types#ulonglong +type PULONGLONG = *mut u64; + +/// Link against Windows' `mincore`. +#[link(name = "mincore")] +extern "system" { + /// Gets the current interrupt-time count. + /// + /// See [`QueryInterruptTime`]. + /// + /// [`QueryInterruptTime`]: https://docs.microsoft.com/en-us/windows/win32/api/realtimeapiset/nf-realtimeapiset-queryinterrupttime + /// + /// Note: we define it ourselves, because it's not actually included in `winapi`. + fn QueryInterruptTime(InterruptTime: PULONGLONG); +} + +/// Windows counts time in a system time unit of 100 nanoseconds. +const SYSTEM_TIME_UNIT: u64 = 100; + +/// The time based on the current interrupt-time count. +/// This includes the suspend time. +/// +/// See [`QueryInterruptTime`]. +/// +/// [`QueryInterruptTime`]: https://docs.microsoft.com/en-us/windows/win32/api/realtimeapiset/nf-realtimeapiset-queryinterrupttime +pub fn now_including_suspend() -> u64 { + let mut interrupt_time = 0; + unsafe { + QueryInterruptTime(&mut interrupt_time); + } + + interrupt_time * SYSTEM_TIME_UNIT +} |