Skip to content

Commit 92613a2

Browse files
authored
Rollup merge of #89926 - the8472:saturate-instant, r=Mark-Simulacrum
make `Instant::{duration_since, elapsed, sub}` saturating and remove workarounds This removes all mutex/atomic-based workarounds for non-monotonic clocks and makes the previously panicking methods saturating instead. Additionally `saturating_duration_since` becomes deprecated since `duration_since` now fills that role. Effectively this moves the fixup from `Instant` construction to the comparisons. This has some observable effects, especially on platforms without monotonic clocks: * Incorrectly ordered Instant comparisons no longer panic in release mode. This could hide some programming errors, but since debug mode still panics tests can still catch them. * `checked_duration_since` will now return `None` in more cases. Previously it only happened when one compared instants obtained in the wrong order or manually created ones. Now it also does on backslides. * non-monotonic intervals will not be transitive, i.e. `b.duration_since(a) + c.duration_since(b) != c.duration_since(a)` The upsides are reduced complexity and lower overhead of `Instant::now`. ## Motivation Currently we must choose between two poisons. One is high worst-case latency and jitter of `Instant::now()` due to explicit synchronization; see #83093 for benchmarks, the worst-case overhead is > 100x. The other is sporadic panics on specific, rare combinations of CPU/hypervisor/operating system due to platform bugs. Use-cases where low-overhead, fine-grained timestamps are needed - such as syscall tracing, performance profiles or sensor data acquisition (drone flight controllers were mentioned in a libs meeting) in multi-threaded programs - are negatively impacted by the synchronization. The panics are user-visible (program crashes), hard to reproduce and can be triggered by any dependency that might be using Instants for any reason. A solution that is fast _and_ doesn't panic is desirable. ---- closes #84448 closes #86470
2 parents 01c4c41 + 37a1fc5 commit 92613a2

File tree

10 files changed

+57
-267
lines changed

10 files changed

+57
-267
lines changed

library/std/src/sys/hermit/time.rs

-8
Original file line numberDiff line numberDiff line change
@@ -115,14 +115,6 @@ impl Instant {
115115
Instant { t: time }
116116
}
117117

118-
pub const fn zero() -> Instant {
119-
Instant { t: Timespec::zero() }
120-
}
121-
122-
pub fn actually_monotonic() -> bool {
123-
true
124-
}
125-
126118
pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
127119
self.t.sub_timespec(&other.t).ok()
128120
}

library/std/src/sys/itron/time.rs

-9
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,6 @@ impl Instant {
1414
}
1515
}
1616

17-
pub const fn zero() -> Instant {
18-
Instant(0)
19-
}
20-
21-
pub fn actually_monotonic() -> bool {
22-
// There are ways to change the system time
23-
false
24-
}
25-
2617
pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
2718
self.0.checked_sub(other.0).map(|ticks| {
2819
// `SYSTIM` is measured in microseconds

library/std/src/sys/sgx/time.rs

-8
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,6 @@ impl Instant {
2525
pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> {
2626
Some(Instant(self.0.checked_sub(*other)?))
2727
}
28-
29-
pub fn actually_monotonic() -> bool {
30-
false
31-
}
32-
33-
pub const fn zero() -> Instant {
34-
Instant(Duration::from_secs(0))
35-
}
3628
}
3729

3830
impl SystemTime {

library/std/src/sys/unix/time.rs

-19
Original file line numberDiff line numberDiff line change
@@ -154,14 +154,6 @@ mod inner {
154154
Instant { t: unsafe { mach_absolute_time() } }
155155
}
156156

157-
pub const fn zero() -> Instant {
158-
Instant { t: 0 }
159-
}
160-
161-
pub fn actually_monotonic() -> bool {
162-
true
163-
}
164-
165157
pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
166158
let diff = self.t.checked_sub(other.t)?;
167159
let info = info();
@@ -296,17 +288,6 @@ mod inner {
296288
Instant { t: now(libc::CLOCK_MONOTONIC) }
297289
}
298290

299-
pub const fn zero() -> Instant {
300-
Instant { t: Timespec::zero() }
301-
}
302-
303-
pub fn actually_monotonic() -> bool {
304-
(cfg!(target_os = "linux") && cfg!(target_arch = "x86_64"))
305-
|| (cfg!(target_os = "linux") && cfg!(target_arch = "x86"))
306-
|| (cfg!(target_os = "linux") && cfg!(target_arch = "aarch64"))
307-
|| cfg!(target_os = "fuchsia")
308-
}
309-
310291
pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
311292
self.t.sub_timespec(&other.t).ok()
312293
}

library/std/src/sys/unsupported/time.rs

-8
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,6 @@ impl Instant {
1313
panic!("time not implemented on this platform")
1414
}
1515

16-
pub const fn zero() -> Instant {
17-
Instant(Duration::from_secs(0))
18-
}
19-
20-
pub fn actually_monotonic() -> bool {
21-
false
22-
}
23-
2416
pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
2517
self.0.checked_sub(other.0)
2618
}

library/std/src/sys/wasi/time.rs

-8
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,6 @@ impl Instant {
2525
Instant(current_time(wasi::CLOCKID_MONOTONIC))
2626
}
2727

28-
pub const fn zero() -> Instant {
29-
Instant(Duration::from_secs(0))
30-
}
31-
32-
pub fn actually_monotonic() -> bool {
33-
true
34-
}
35-
3628
pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
3729
self.0.checked_sub(other.0)
3830
}

library/std/src/sys/windows/time.rs

-8
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,6 @@ impl Instant {
4141
perf_counter::PerformanceCounterInstant::now().into()
4242
}
4343

44-
pub fn actually_monotonic() -> bool {
45-
false
46-
}
47-
48-
pub const fn zero() -> Instant {
49-
Instant { t: Duration::from_secs(0) }
50-
}
51-
5244
pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
5345
// On windows there's a threshold below which we consider two timestamps
5446
// equivalent due to measurement error. For more details + doc link,

library/std/src/time.rs

+54-55
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
3232
#![stable(feature = "time", since = "1.3.0")]
3333

34-
mod monotonic;
3534
#[cfg(test)]
3635
mod tests;
3736

@@ -50,8 +49,8 @@ pub use core::time::FromFloatSecsError;
5049
/// A measurement of a monotonically nondecreasing clock.
5150
/// Opaque and useful only with [`Duration`].
5251
///
53-
/// Instants are always guaranteed to be no less than any previously measured
54-
/// instant when created, and are often useful for tasks such as measuring
52+
/// Instants are always guaranteed, barring [platform bugs], to be no less than any previously
53+
/// measured instant when created, and are often useful for tasks such as measuring
5554
/// benchmarks or timing how long an operation takes.
5655
///
5756
/// Note, however, that instants are **not** guaranteed to be **steady**. In other
@@ -84,6 +83,8 @@ pub use core::time::FromFloatSecsError;
8483
/// }
8584
/// ```
8685
///
86+
/// [platform bugs]: Instant#monotonicity
87+
///
8788
/// # OS-specific behaviors
8889
///
8990
/// An `Instant` is a wrapper around system-specific types and it may behave
@@ -125,6 +126,26 @@ pub use core::time::FromFloatSecsError;
125126
/// > structure cannot represent the new point in time.
126127
///
127128
/// [`add`]: Instant::add
129+
///
130+
/// ## Monotonicity
131+
///
132+
/// On all platforms `Instant` will try to use an OS API that guarantees monotonic behavior
133+
/// if available, which is the case for all [tier 1] platforms.
134+
/// In practice such guarantees are – under rare circumstances – broken by hardware, virtualization
135+
/// or operating system bugs. To work around these bugs and platforms not offering monotonic clocks
136+
/// [`duration_since`], [`elapsed`] and [`sub`] saturate to zero. In older Rust versions this
137+
/// lead to a panic instead. [`checked_duration_since`] can be used to detect and handle situations
138+
/// where monotonicity is violated, or `Instant`s are subtracted in the wrong order.
139+
///
140+
/// This workaround obscures programming errors where earlier and later instants are accidentally
141+
/// swapped. For this reason future rust versions may reintroduce panics.
142+
///
143+
/// [tier 1]: https://doc.rust-lang.org/rustc/platform-support.html
144+
/// [`duration_since`]: Instant::duration_since
145+
/// [`elapsed`]: Instant::elapsed
146+
/// [`sub`]: Instant::sub
147+
/// [`checked_duration_since`]: Instant::checked_duration_since
148+
///
128149
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
129150
#[stable(feature = "time2", since = "1.8.0")]
130151
pub struct Instant(time::Instant);
@@ -247,59 +268,19 @@ impl Instant {
247268
#[must_use]
248269
#[stable(feature = "time2", since = "1.8.0")]
249270
pub fn now() -> Instant {
250-
let os_now = time::Instant::now();
251-
252-
// And here we come upon a sad state of affairs. The whole point of
253-
// `Instant` is that it's monotonically increasing. We've found in the
254-
// wild, however, that it's not actually monotonically increasing for
255-
// one reason or another. These appear to be OS and hardware level bugs,
256-
// and there's not really a whole lot we can do about them. Here's a
257-
// taste of what we've found:
258-
//
259-
// * #48514 - OpenBSD, x86_64
260-
// * #49281 - linux arm64 and s390x
261-
// * #51648 - windows, x86
262-
// * #56560 - windows, x86_64, AWS
263-
// * #56612 - windows, x86, vm (?)
264-
// * #56940 - linux, arm64
265-
// * https://bugzilla.mozilla.org/show_bug.cgi?id=1487778 - a similar
266-
// Firefox bug
267-
//
268-
// It seems that this just happens a lot in the wild.
269-
// We're seeing panics across various platforms where consecutive calls
270-
// to `Instant::now`, such as via the `elapsed` function, are panicking
271-
// as they're going backwards. Placed here is a last-ditch effort to try
272-
// to fix things up. We keep a global "latest now" instance which is
273-
// returned instead of what the OS says if the OS goes backwards.
274-
//
275-
// To hopefully mitigate the impact of this, a few platforms are
276-
// excluded as "these at least haven't gone backwards yet".
277-
//
278-
// While issues have been seen on arm64 platforms the Arm architecture
279-
// requires that the counter monotonically increases and that it must
280-
// provide a uniform view of system time (e.g. it must not be possible
281-
// for a core to receive a message from another core with a time stamp
282-
// and observe time going backwards (ARM DDI 0487G.b D11.1.2). While
283-
// there have been a few 64bit SoCs that have bugs which cause time to
284-
// not monoticially increase, these have been fixed in the Linux kernel
285-
// and we shouldn't penalize all Arm SoCs for those who refuse to
286-
// update their kernels:
287-
// SUN50I_ERRATUM_UNKNOWN1 - Allwinner A64 / Pine A64 - fixed in 5.1
288-
// FSL_ERRATUM_A008585 - Freescale LS2080A/LS1043A - fixed in 4.10
289-
// HISILICON_ERRATUM_161010101 - Hisilicon 1610 - fixed in 4.11
290-
// ARM64_ERRATUM_858921 - Cortex A73 - fixed in 4.12
291-
if time::Instant::actually_monotonic() {
292-
return Instant(os_now);
293-
}
294-
295-
Instant(monotonic::monotonize(os_now))
271+
Instant(time::Instant::now())
296272
}
297273

298-
/// Returns the amount of time elapsed from another instant to this one.
274+
/// Returns the amount of time elapsed from another instant to this one,
275+
/// or zero duration if that instant is later than this one.
299276
///
300277
/// # Panics
301278
///
302-
/// This function will panic if `earlier` is later than `self`.
279+
/// Previous rust versions panicked when `earlier` was later than `self`. Currently this
280+
/// method saturates. Future versions may reintroduce the panic in some circumstances.
281+
/// See [Monotonicity].
282+
///
283+
/// [Monotonicity]: Instant#monotonicity
303284
///
304285
/// # Examples
305286
///
@@ -311,16 +292,22 @@ impl Instant {
311292
/// sleep(Duration::new(1, 0));
312293
/// let new_now = Instant::now();
313294
/// println!("{:?}", new_now.duration_since(now));
295+
/// println!("{:?}", now.duration_since(new_now)); // 0ns
314296
/// ```
315297
#[must_use]
316298
#[stable(feature = "time2", since = "1.8.0")]
317299
pub fn duration_since(&self, earlier: Instant) -> Duration {
318-
self.0.checked_sub_instant(&earlier.0).expect("supplied instant is later than self")
300+
self.checked_duration_since(earlier).unwrap_or_default()
319301
}
320302

321303
/// Returns the amount of time elapsed from another instant to this one,
322304
/// or None if that instant is later than this one.
323305
///
306+
/// Due to [monotonicity bugs], even under correct logical ordering of the passed `Instant`s,
307+
/// this method can return `None`.
308+
///
309+
/// [monotonicity bugs]: Instant#monotonicity
310+
///
324311
/// # Examples
325312
///
326313
/// ```no_run
@@ -364,9 +351,11 @@ impl Instant {
364351
///
365352
/// # Panics
366353
///
367-
/// This function may panic if the current time is earlier than this
368-
/// instant, which is something that can happen if an `Instant` is
369-
/// produced synthetically.
354+
/// Previous rust versions panicked when self was earlier than the current time. Currently this
355+
/// method returns a Duration of zero in that case. Future versions may reintroduce the panic.
356+
/// See [Monotonicity].
357+
///
358+
/// [Monotonicity]: Instant#monotonicity
370359
///
371360
/// # Examples
372361
///
@@ -442,6 +431,16 @@ impl SubAssign<Duration> for Instant {
442431
impl Sub<Instant> for Instant {
443432
type Output = Duration;
444433

434+
/// Returns the amount of time elapsed from another instant to this one,
435+
/// or zero duration if that instant is later than this one.
436+
///
437+
/// # Panics
438+
///
439+
/// Previous rust versions panicked when `other` was later than `self`. Currently this
440+
/// method saturates. Future versions may reintroduce the panic in some circumstances.
441+
/// See [Monotonicity].
442+
///
443+
/// [Monotonicity]: Instant#monotonicity
445444
fn sub(self, other: Instant) -> Duration {
446445
self.duration_since(other)
447446
}

0 commit comments

Comments
 (0)