summaryrefslogtreecommitdiffstats
path: root/library/std/src/sys/itron/time.rs
blob: 427ea0d80e10739aff897e15a162feb833a60d9a (plain)
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
use super::{abi, error::expect_success};
use crate::{mem::MaybeUninit, time::Duration};

#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
pub struct Instant(abi::SYSTIM);

impl Instant {
    pub fn now() -> Instant {
        // Safety: The provided pointer is valid
        unsafe {
            let mut out = MaybeUninit::uninit();
            expect_success(abi::get_tim(out.as_mut_ptr()), &"get_tim");
            Instant(out.assume_init())
        }
    }

    pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
        self.0.checked_sub(other.0).map(|ticks| {
            // `SYSTIM` is measured in microseconds
            Duration::from_micros(ticks)
        })
    }

    pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> {
        // `SYSTIM` is measured in microseconds
        let ticks = other.as_micros();

        Some(Instant(self.0.checked_add(ticks.try_into().ok()?)?))
    }

    pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> {
        // `SYSTIM` is measured in microseconds
        let ticks = other.as_micros();

        Some(Instant(self.0.checked_sub(ticks.try_into().ok()?)?))
    }
}

/// Split `Duration` into zero or more `RELTIM`s.
#[inline]
pub fn dur2reltims(dur: Duration) -> impl Iterator<Item = abi::RELTIM> {
    // `RELTIM` is microseconds
    let mut ticks = dur.as_micros();

    crate::iter::from_fn(move || {
        if ticks == 0 {
            None
        } else if ticks <= abi::TMAX_RELTIM as u128 {
            Some(crate::mem::replace(&mut ticks, 0) as abi::RELTIM)
        } else {
            ticks -= abi::TMAX_RELTIM as u128;
            Some(abi::TMAX_RELTIM)
        }
    })
}

/// Split `Duration` into one or more `TMO`s.
#[inline]
fn dur2tmos(dur: Duration) -> impl Iterator<Item = abi::TMO> {
    // `TMO` is microseconds
    let mut ticks = dur.as_micros();
    let mut end = false;

    crate::iter::from_fn(move || {
        if end {
            None
        } else if ticks <= abi::TMAX_RELTIM as u128 {
            end = true;
            Some(crate::mem::replace(&mut ticks, 0) as abi::TMO)
        } else {
            ticks -= abi::TMAX_RELTIM as u128;
            Some(abi::TMAX_RELTIM)
        }
    })
}

/// Split `Duration` into one or more API calls with timeout.
#[inline]
pub fn with_tmos(dur: Duration, mut f: impl FnMut(abi::TMO) -> abi::ER) -> abi::ER {
    let mut er = abi::E_TMOUT;
    for tmo in dur2tmos(dur) {
        er = f(tmo);
        if er != abi::E_TMOUT {
            break;
        }
    }
    er
}

/// Split `Duration` into one or more API calls with timeout. This function can
/// handle spurious wakeups.
#[inline]
pub fn with_tmos_strong(dur: Duration, mut f: impl FnMut(abi::TMO) -> abi::ER) -> abi::ER {
    // `TMO` and `SYSTIM` are microseconds.
    // Clamp at `SYSTIM::MAX` for performance reasons. This shouldn't cause
    // a problem in practice. (`u64::MAX` μs ≈ 584942 years)
    let ticks = dur.as_micros().min(abi::SYSTIM::MAX as u128) as abi::SYSTIM;

    let start = Instant::now().0;
    let mut elapsed = 0;
    let mut er = abi::E_TMOUT;
    while elapsed <= ticks {
        er = f(elapsed.min(abi::TMAX_RELTIM as abi::SYSTIM) as abi::TMO);
        if er != abi::E_TMOUT {
            break;
        }
        elapsed = Instant::now().0.wrapping_sub(start);
    }

    er
}

#[cfg(test)]
mod tests;