]> git.proxmox.com Git - rustc.git/blame - library/std/src/time/monotonic.rs
New upstream version 1.59.0+dfsg1
[rustc.git] / library / std / src / time / monotonic.rs
CommitLineData
94222f64
XL
1use crate::sys::time;
2
3#[inline]
4pub(super) fn monotonize(raw: time::Instant) -> time::Instant {
5 inner::monotonize(raw)
6}
7
c295e0f8 8#[cfg(any(all(target_has_atomic = "64", not(target_has_atomic = "128")), target_arch = "aarch64"))]
94222f64
XL
9pub mod inner {
10 use crate::sync::atomic::AtomicU64;
11 use crate::sync::atomic::Ordering::*;
12 use crate::sys::time;
13 use crate::time::Duration;
14
15 pub(in crate::time) const ZERO: time::Instant = time::Instant::zero();
16
17 // bits 30 and 31 are never used since the nanoseconds part never exceeds 10^9
18 const UNINITIALIZED: u64 = 0b11 << 30;
19 static MONO: AtomicU64 = AtomicU64::new(UNINITIALIZED);
20
21 #[inline]
22 pub(super) fn monotonize(raw: time::Instant) -> time::Instant {
23 monotonize_impl(&MONO, raw)
24 }
25
26 #[inline]
27 pub(in crate::time) fn monotonize_impl(mono: &AtomicU64, raw: time::Instant) -> time::Instant {
28 let delta = raw.checked_sub_instant(&ZERO).unwrap();
29 let secs = delta.as_secs();
30 // occupies no more than 30 bits (10^9 seconds)
31 let nanos = delta.subsec_nanos() as u64;
32
33 // This wraps around every 136 years (2^32 seconds).
34 // To detect backsliding we use wrapping arithmetic and declare forward steps smaller
35 // than 2^31 seconds as expected and everything else as a backslide which will be
36 // monotonized.
37 // This could be a problem for programs that call instants at intervals greater
38 // than 68 years. Interstellar probes may want to ensure that actually_monotonic() is true.
39 let packed = (secs << 32) | nanos;
c295e0f8
XL
40 let updated = mono.fetch_update(Relaxed, Relaxed, |old| {
41 (old == UNINITIALIZED || packed.wrapping_sub(old) < u64::MAX / 2).then_some(packed)
42 });
43 match updated {
44 Ok(_) => raw,
45 Err(newer) => {
46 // Backslide occurred. We reconstruct monotonized time from the upper 32 bit of the
47 // passed in value and the 64bits loaded from the atomic
48 let seconds_lower = newer >> 32;
49 let mut seconds_upper = secs & 0xffff_ffff_0000_0000;
50 if secs & 0xffff_ffff > seconds_lower {
51 // Backslide caused the lower 32bit of the seconds part to wrap.
52 // This must be the case because the seconds part is larger even though
53 // we are in the backslide branch, i.e. the seconds count should be smaller or equal.
54 //
55 // We assume that backslides are smaller than 2^32 seconds
56 // which means we need to add 1 to the upper half to restore it.
57 //
58 // Example:
59 // most recent observed time: 0xA1_0000_0000_0000_0000u128
60 // bits stored in AtomicU64: 0x0000_0000_0000_0000u64
61 // backslide by 1s
62 // caller time is 0xA0_ffff_ffff_0000_0000u128
63 // -> we can fix up the upper half time by adding 1 << 32
64 seconds_upper = seconds_upper.wrapping_add(0x1_0000_0000);
65 }
66 let secs = seconds_upper | seconds_lower;
67 let nanos = newer as u32;
68 ZERO.checked_add_duration(&Duration::new(secs, nanos)).unwrap()
94222f64 69 }
94222f64
XL
70 }
71 }
72}
73
c295e0f8 74#[cfg(all(target_has_atomic = "128", not(target_arch = "aarch64")))]
94222f64
XL
75pub mod inner {
76 use crate::sync::atomic::AtomicU128;
77 use crate::sync::atomic::Ordering::*;
78 use crate::sys::time;
79 use crate::time::Duration;
80
81 const ZERO: time::Instant = time::Instant::zero();
82 static MONO: AtomicU128 = AtomicU128::new(0);
83
84 #[inline]
85 pub(super) fn monotonize(raw: time::Instant) -> time::Instant {
86 let delta = raw.checked_sub_instant(&ZERO).unwrap();
87 // Split into seconds and nanos since Duration doesn't have a
88 // constructor that takes a u128
89 let secs = delta.as_secs() as u128;
90 let nanos = delta.subsec_nanos() as u128;
91 let timestamp: u128 = secs << 64 | nanos;
92 let timestamp = MONO.fetch_max(timestamp, Relaxed).max(timestamp);
93 let secs = (timestamp >> 64) as u64;
94 let nanos = timestamp as u32;
95 ZERO.checked_add_duration(&Duration::new(secs, nanos)).unwrap()
96 }
97}
98
99#[cfg(not(any(target_has_atomic = "64", target_has_atomic = "128")))]
100pub mod inner {
101 use crate::cmp;
102 use crate::sys::time;
103 use crate::sys_common::mutex::StaticMutex;
104
105 #[inline]
106 pub(super) fn monotonize(os_now: time::Instant) -> time::Instant {
107 static LOCK: StaticMutex = StaticMutex::new();
108 static mut LAST_NOW: time::Instant = time::Instant::zero();
109 unsafe {
110 let _lock = LOCK.lock();
111 let now = cmp::max(LAST_NOW, os_now);
112 LAST_NOW = now;
113 now
114 }
115 }
116}