diff --git a/library/std/src/sys/common/mod.rs b/library/std/src/sys/common/mod.rs index ff64d2aa82515..550a4b1b94b93 100644 --- a/library/std/src/sys/common/mod.rs +++ b/library/std/src/sys/common/mod.rs @@ -11,3 +11,4 @@ #![allow(dead_code)] pub mod alloc; +pub mod time; diff --git a/library/std/src/sys/common/time.rs b/library/std/src/sys/common/time.rs new file mode 100644 index 0000000000000..0c22478a25e65 --- /dev/null +++ b/library/std/src/sys/common/time.rs @@ -0,0 +1,35 @@ +use crate::env; +use crate::sync::atomic::{AtomicU8, Ordering}; + +#[repr(u8)] +#[derive(Copy, Clone)] +pub enum InstantReliability { + // 0 is the placeholder for initialization pending + Default = 1, + AssumeMonotonic = 2, + AssumeBroken = 3, +} + +static INSTANT_RELIABILITY: AtomicU8 = AtomicU8::new(0); + +pub fn instant_reliability_override() -> InstantReliability { + let current = INSTANT_RELIABILITY.load(Ordering::Relaxed); + + if current == 0 { + let new = match env::var("RUST_CLOCK_ASSUME_MONOTONIC").as_deref() { + Ok("1") => InstantReliability::AssumeMonotonic, + Ok("0") => InstantReliability::AssumeBroken, + Ok(_) => { + eprintln!("unsupported value in RUST_CLOCK_ASSUME_MONOTONIC; using default"); + InstantReliability::Default + } + _ => InstantReliability::Default, + }; + INSTANT_RELIABILITY.store(new as u8, Ordering::Relaxed); + new + } else { + // SAFETY: The enum has a compatible layout and after initialization only values + // cast from enum variants are stored in the atomic static. + unsafe { crate::mem::transmute(current) } + } +} diff --git a/library/std/src/sys/unix/time.rs b/library/std/src/sys/unix/time.rs index 23a5c81c0053b..25c4f88828eac 100644 --- a/library/std/src/sys/unix/time.rs +++ b/library/std/src/sys/unix/time.rs @@ -273,12 +273,12 @@ mod inner { #[cfg(not(any(target_os = "macos", target_os = "ios")))] mod inner { + use super::Timespec; use crate::fmt; + use crate::sys::common::time::{instant_reliability_override, InstantReliability}; use crate::sys::cvt; use crate::time::Duration; - use super::Timespec; - #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Instant { t: Timespec, @@ -301,9 +301,15 @@ mod inner { } pub fn actually_monotonic() -> bool { - (cfg!(target_os = "linux") && cfg!(target_arch = "x86_64")) - || (cfg!(target_os = "linux") && cfg!(target_arch = "x86")) - || cfg!(target_os = "fuchsia") + match instant_reliability_override() { + InstantReliability::Default => { + (cfg!(target_os = "linux") && cfg!(target_arch = "x86_64")) + || (cfg!(target_os = "linux") && cfg!(target_arch = "x86")) + || cfg!(target_os = "fuchsia") + } + InstantReliability::AssumeMonotonic => true, + InstantReliability::AssumeBroken => false, + } } pub fn checked_sub_instant(&self, other: &Instant) -> Option { diff --git a/library/std/src/sys/windows/time.rs b/library/std/src/sys/windows/time.rs index 91e4f7654840d..1778203fe5335 100644 --- a/library/std/src/sys/windows/time.rs +++ b/library/std/src/sys/windows/time.rs @@ -3,6 +3,7 @@ use crate::convert::TryInto; use crate::fmt; use crate::mem; use crate::sys::c; +use crate::sys::common::time::{instant_reliability_override, InstantReliability}; use crate::time::Duration; use core::hash::{Hash, Hasher}; @@ -42,7 +43,11 @@ impl Instant { } pub fn actually_monotonic() -> bool { - false + match instant_reliability_override() { + InstantReliability::Default => false, + InstantReliability::AssumeMonotonic => true, + InstantReliability::AssumeBroken => false, + } } pub const fn zero() -> Instant { diff --git a/library/std/src/time.rs b/library/std/src/time.rs index 89addae078948..eea51e043d961 100644 --- a/library/std/src/time.rs +++ b/library/std/src/time.rs @@ -103,6 +103,28 @@ pub use core::time::Duration; /// > structure cannot represent the new point in time. /// /// [`add`]: Instant::add +/// +/// ## Reliability +/// +/// On platforms where the underlying system call's API contract guarantees monotonicity +/// Instant may rely on that property. Otherwise it will add its own synchronization +/// to ensure monotonicity. Sometimes operating system guarantees are broken by hardware +/// or virtualization bugs. This can manifest in Duration between an older and newer Instant +/// appearing to be negative which results in a panic. +/// +/// On windows and some unix systems you can override rust's choice to add +/// its own synchronization or rely on the operating system by setting the environment variable +/// `RUST_CLOCK_ASSUME_MONOTONIC` to `1` (trust the system-provided time) or `0` +/// (apply additional synchronization). The value of the variable is only read +/// the first time an Instant is created. Altering it later will have no effect. +/// +/// > Note: The environment variable is experimental and meant to diagnose bugs. +/// > It is not part of the normal Rust stability guarantees. +/// +/// If you encounter a case where additional synchronization is necessary please +/// [file an issue] +/// +/// [file an issue]: https://github.com/rust-lang/rust/issues #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[stable(feature = "time2", since = "1.8.0")] pub struct Instant(time::Instant); @@ -327,7 +349,9 @@ impl Instant { /// /// This function may panic if the current time is earlier than this /// instant, which is something that can happen if an `Instant` is - /// produced synthetically. + /// produced synthetically or due to [hardware bugs] + /// + /// [hardware bugs]: Instant#reliability /// /// # Examples ///