1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
use crate::sys::time;

#[inline]
pub(super) fn monotonize(raw: time::Instant) -> time::Instant {
    inner::monotonize(raw)
}

#[cfg(any(all(target_has_atomic = "64", not(target_has_atomic = "128")), target_arch = "aarch64"))]
pub mod inner {
    use crate::sync::atomic::AtomicU64;
    use crate::sync::atomic::Ordering::*;
    use crate::sys::time;
    use crate::time::Duration;

    pub(in crate::time) const ZERO: time::Instant = time::Instant::zero();

    // 位 30 和 31 从未使用过,因为纳秒部分从未超过 10^9
    const UNINITIALIZED: u64 = 0b11 << 30;
    static MONO: AtomicU64 = AtomicU64::new(UNINITIALIZED);

    #[inline]
    pub(super) fn monotonize(raw: time::Instant) -> time::Instant {
        monotonize_impl(&MONO, raw)
    }

    #[inline]
    pub(in crate::time) fn monotonize_impl(mono: &AtomicU64, raw: time::Instant) -> time::Instant {
        let delta = raw.checked_sub_instant(&ZERO).unwrap();
        let secs = delta.as_secs();
        // 占用不超过 30 位 (10^9 秒)
        let nanos = delta.subsec_nanos() as u64;

        // 这每 136 年 (2^32 秒) 循环一次。
        // 为了检测后退,我们使用包装算法并按预期声明小于 2^31 秒的前向步长,并将其他所有内容声明为将单调化的后退。
        //
        // 对于以大于 68 年的间隔调用即时的程序,这可能是一个问题。星际探测器可能想要确保 actually_monotonic() 是真实的。
        //
        //
        let packed = (secs << 32) | nanos;
        let updated = mono.fetch_update(Relaxed, Relaxed, |old| {
            (old == UNINITIALIZED || packed.wrapping_sub(old) < u64::MAX / 2).then_some(packed)
        });
        match updated {
            Ok(_) => raw,
            Err(newer) => {
                // 发生了倒退。
                // 我们从传入值的高 32 位和从原子加载的 64 位重建单调时间
                let seconds_lower = newer >> 32;
                let mut seconds_upper = secs & 0xffff_ffff_0000_0000;
                if secs & 0xffff_ffff > seconds_lower {
                    // Backslide 导致秒部分的低 32 位换行。
                    // 这一定是这种情况,因为即使我们在 backslide 分支中,秒部分也更大,即秒数应该更小或相等。
                    //
                    // 我们假设后退小于 2^32 秒,这意味着我们需要在上半部分加 1 才能恢复它。
                    //
                    // Example:
                    // 最近观察到的时间: 0xA1_0000_0000_0000_0000u128 位存储在 AtomicU64 中: 0x0000_0000_0000_0000u64 倒退 1s 调用者时间是 0xA0_ffff_ffff_0000u12080
                    //
                    // -> 我们可以通过添加 1 << 32 来修复上半场
                    //
                    //
                    //
                    //
                    seconds_upper = seconds_upper.wrapping_add(0x1_0000_0000);
                }
                let secs = seconds_upper | seconds_lower;
                let nanos = newer as u32;
                ZERO.checked_add_duration(&Duration::new(secs, nanos)).unwrap()
            }
        }
    }
}

#[cfg(all(target_has_atomic = "128", not(target_arch = "aarch64")))]
pub mod inner {
    use crate::sync::atomic::AtomicU128;
    use crate::sync::atomic::Ordering::*;
    use crate::sys::time;
    use crate::time::Duration;

    const ZERO: time::Instant = time::Instant::zero();
    static MONO: AtomicU128 = AtomicU128::new(0);

    #[inline]
    pub(super) fn monotonize(raw: time::Instant) -> time::Instant {
        let delta = raw.checked_sub_instant(&ZERO).unwrap();
        // 由于 Duration 没有采用 u128 的构造函数,因此分为秒和纳秒
        //
        let secs = delta.as_secs() as u128;
        let nanos = delta.subsec_nanos() as u128;
        let timestamp: u128 = secs << 64 | nanos;
        let timestamp = MONO.fetch_max(timestamp, Relaxed).max(timestamp);
        let secs = (timestamp >> 64) as u64;
        let nanos = timestamp as u32;
        ZERO.checked_add_duration(&Duration::new(secs, nanos)).unwrap()
    }
}

#[cfg(not(any(target_has_atomic = "64", target_has_atomic = "128")))]
pub mod inner {
    use crate::cmp;
    use crate::sys::time;
    use crate::sys_common::mutex::StaticMutex;

    #[inline]
    pub(super) fn monotonize(os_now: time::Instant) -> time::Instant {
        static LOCK: StaticMutex = StaticMutex::new();
        static mut LAST_NOW: time::Instant = time::Instant::zero();
        unsafe {
            let _lock = LOCK.lock();
            let now = cmp::max(LAST_NOW, os_now);
            LAST_NOW = now;
            now
        }
    }
}