]>
Commit | Line | Data |
---|---|---|
94222f64 XL |
1 | use crate::sys::time; |
2 | ||
3 | #[inline] | |
4 | pub(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 |
9 | pub 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 |
75 | pub 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")))] | |
100 | pub 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 | } |