|
| 1 | +use crate::sys::time; |
| 2 | + |
| 3 | +#[inline] |
| 4 | +pub(super) fn monotonize(raw: time::Instant) -> time::Instant { |
| 5 | + inner::monotonize(raw) |
| 6 | +} |
| 7 | + |
| 8 | +#[cfg(all(target_has_atomic = "64", not(target_has_atomic = "128")))] |
| 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 | + const ZERO: time::Instant = time::Instant::zero(); |
| 16 | + |
| 17 | + // bits 30 and 31 are never used since the seconds part never exceeds 10^9 |
| 18 | + const UNINITIALIZED: u64 = 0xff00_0000; |
| 19 | + static MONO: AtomicU64 = AtomicU64::new(UNINITIALIZED); |
| 20 | + |
| 21 | + #[inline] |
| 22 | + pub(super) fn monotonize(raw: time::Instant) -> time::Instant { |
| 23 | + let delta = raw.checked_sub_instant(&ZERO).unwrap(); |
| 24 | + let secs = delta.as_secs(); |
| 25 | + // occupies no more than 30 bits (10^9 seconds) |
| 26 | + let nanos = delta.subsec_nanos() as u64; |
| 27 | + |
| 28 | + // This wraps around every 136 years (2^32 seconds). |
| 29 | + // To detect backsliding we use wrapping arithmetic and declare forward steps smaller |
| 30 | + // than 2^31 seconds as expected and everything else as a backslide which will be |
| 31 | + // monotonized. |
| 32 | + // This could be a problem for programs that call instants at intervals greater |
| 33 | + // than 68 years. Interstellar probes may want to ensure that actually_monotonic() is true. |
| 34 | + let packed = (secs << 32) | nanos; |
| 35 | + let old = MONO.load(Relaxed); |
| 36 | + |
| 37 | + if packed == UNINITIALIZED || packed.wrapping_sub(old) < u64::MAX / 2 { |
| 38 | + MONO.store(packed, Relaxed); |
| 39 | + raw |
| 40 | + } else { |
| 41 | + // Backslide occurred. We reconstruct monotonized time by assuming the clock will never |
| 42 | + // backslide more than 2`32 seconds which means we can reuse the upper 32bits from |
| 43 | + // the seconds. |
| 44 | + let secs = (secs & 0xffff_ffff << 32) | old >> 32; |
| 45 | + let nanos = old as u32; |
| 46 | + ZERO.checked_add_duration(&Duration::new(secs, nanos)).unwrap() |
| 47 | + } |
| 48 | + } |
| 49 | +} |
| 50 | + |
| 51 | +#[cfg(target_has_atomic = "128")] |
| 52 | +pub mod inner { |
| 53 | + use crate::sync::atomic::AtomicU128; |
| 54 | + use crate::sync::atomic::Ordering::*; |
| 55 | + use crate::sys::time; |
| 56 | + use crate::time::Duration; |
| 57 | + |
| 58 | + const ZERO: time::Instant = time::Instant::zero(); |
| 59 | + static MONO: AtomicU128 = AtomicU128::new(0); |
| 60 | + |
| 61 | + #[inline] |
| 62 | + pub(super) fn monotonize(raw: time::Instant) -> time::Instant { |
| 63 | + let delta = raw.checked_sub_instant(&ZERO).unwrap(); |
| 64 | + // Split into seconds and nanos since Duration doesn't have a |
| 65 | + // constructor that takes an u128 |
| 66 | + let secs = delta.as_secs() as u128; |
| 67 | + let nanos = delta.subsec_nanos() as u128; |
| 68 | + let timestamp: u128 = secs << 64 | nanos; |
| 69 | + let timestamp = MONO.fetch_max(timestamp, Relaxed).max(timestamp); |
| 70 | + let secs = (timestamp >> 64) as u64; |
| 71 | + let nanos = timestamp as u32; |
| 72 | + ZERO.checked_add_duration(&Duration::new(secs, nanos)).unwrap() |
| 73 | + } |
| 74 | +} |
| 75 | + |
| 76 | +#[cfg(not(any(target_has_atomic = "64", target_has_atomic = "128")))] |
| 77 | +pub mod inner { |
| 78 | + use crate::cmp; |
| 79 | + use crate::sys::time; |
| 80 | + use crate::sys_common::mutex::StaticMutex; |
| 81 | + |
| 82 | + #[inline] |
| 83 | + pub(super) fn monotonize(os_now: time::Instant) -> time::Instant { |
| 84 | + static LOCK: StaticMutex = StaticMutex::new(); |
| 85 | + static mut LAST_NOW: time::Instant = time::Instant::zero(); |
| 86 | + unsafe { |
| 87 | + let _lock = LOCK.lock(); |
| 88 | + let now = cmp::max(LAST_NOW, os_now); |
| 89 | + LAST_NOW = now; |
| 90 | + now |
| 91 | + } |
| 92 | + } |
| 93 | +} |
0 commit comments