summaryrefslogtreecommitdiffstats
path: root/third_party/rust/zeitstempel/src
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/zeitstempel/src')
-rw-r--r--third_party/rust/zeitstempel/src/fallback.rs31
-rw-r--r--third_party/rust/zeitstempel/src/lib.rs105
-rw-r--r--third_party/rust/zeitstempel/src/linux.rs24
-rw-r--r--third_party/rust/zeitstempel/src/mac.rs17
-rw-r--r--third_party/rust/zeitstempel/src/win.rs42
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
+}