veecle_osal_std/
time.rs

1//! Reexport of `std::time` modules.
2
3use std::future::{Future, IntoFuture};
4
5use futures::future::{Either, TryFutureExt};
6use veecle_osal_api::Error;
7pub use veecle_osal_api::time::{
8    Duration, Exceeded, Instant, Interval, SystemTime, SystemTimeError, SystemTimeSync,
9    TimeAbstraction,
10};
11
12/// Implements the [`TimeAbstraction`] trait for standard Rust.
13///
14/// This implementation uses [`tokio`] under the hood and, therefore, all time-based
15/// operations will rely on the Tokio runtime.
16#[derive(Debug)]
17pub struct Time;
18
19impl TimeAbstraction for Time {
20    fn now() -> Instant {
21        use std::sync::LazyLock;
22        static EPOCH: LazyLock<std::time::Instant> = LazyLock::new(std::time::Instant::now);
23        Instant::MIN
24            + Duration::try_from(EPOCH.elapsed())
25                .expect("time elapsed since start is less than 2^64-1 microseconds")
26    }
27
28    async fn sleep(duration: Duration) -> Result<(), Error> {
29        let duration = std::time::Duration::from_millis(duration.as_millis());
30        tokio::time::sleep(duration).await;
31        Ok(())
32    }
33
34    async fn sleep_until(deadline: Instant) -> Result<(), Error> {
35        Self::sleep(
36            deadline
37                .duration_since(Self::now())
38                .unwrap_or(Duration::ZERO),
39        )
40        .await
41    }
42
43    fn timeout_at<F>(
44        deadline: Instant,
45        future: F,
46    ) -> impl Future<Output = Result<F::Output, Either<Exceeded, Error>>>
47    where
48        Self: Sized,
49        F: IntoFuture,
50    {
51        let duration = deadline
52            .duration_since(Self::now())
53            .map(|duration| std::time::Duration::from_millis(duration.as_millis()))
54            .unwrap_or(std::time::Duration::ZERO);
55
56        tokio::time::timeout(duration, future).map_err(|_elapsed| Either::Left(Exceeded))
57    }
58
59    fn interval(period: Duration) -> impl Interval
60    where
61        Self: Sized,
62    {
63        struct IntervalInternal(tokio::time::Interval);
64
65        impl Interval for IntervalInternal {
66            async fn tick(&mut self) -> Result<(), Error> {
67                self.0.tick().await;
68                Ok(())
69            }
70        }
71
72        let period = std::time::Duration::from_millis(period.as_millis());
73        IntervalInternal(tokio::time::interval(period))
74    }
75}
76
77impl SystemTime for Time {
78    fn duration_since_epoch() -> Result<Duration, SystemTimeError> {
79        let std_duration_since_epoch = std::time::SystemTime::now()
80            .duration_since(std::time::SystemTime::UNIX_EPOCH)
81            .expect("SystemTime::now() should not be less then UNIX_EPOCH");
82        Ok(Duration::from_millis(
83            std_duration_since_epoch.as_millis() as u64
84        ))
85    }
86}
87
88#[cfg(all(test, not(miri)))]
89mod tests {
90    use core::pin::pin;
91
92    use futures::future::{Either, FutureExt};
93    use veecle_osal_api::time::{Duration, Exceeded, Interval, SystemTime, TimeAbstraction};
94
95    use crate::time::Time;
96
97    #[test]
98    fn test_std_system_time_duration_since_epoch() {
99        let a = Time::duration_since_epoch().unwrap_or_default();
100        let b = Time::duration_since_epoch().unwrap_or_default();
101        assert!(b.as_millis() - a.as_millis() < 100);
102
103        let sleep_time = 200;
104        std::thread::sleep(std::time::Duration::from_millis(sleep_time));
105
106        let c = Time::duration_since_epoch().unwrap_or_default();
107        assert!(c.as_millis() - a.as_millis() >= sleep_time);
108        assert!(c.as_millis() - a.as_millis() < sleep_time + 100);
109    }
110
111    #[tokio::test(start_paused = true)]
112    async fn sleep_until_smoke_test() {
113        let mut sleep = pin!(Time::sleep_until(Time::now() + Duration::from_secs(5)));
114
115        assert!(sleep.as_mut().now_or_never().is_none());
116
117        tokio::time::advance(std::time::Duration::from_secs(2)).await;
118
119        assert!(sleep.as_mut().now_or_never().is_none());
120
121        tokio::time::advance(std::time::Duration::from_secs(4)).await;
122
123        assert!(matches!(sleep.as_mut().now_or_never(), Some(Ok(()))));
124    }
125
126    #[tokio::test(start_paused = true)]
127    async fn sleep_smoke_test() {
128        let mut sleep = pin!(Time::sleep(Duration::from_secs(5)));
129
130        assert!(sleep.as_mut().now_or_never().is_none());
131
132        tokio::time::advance(std::time::Duration::from_secs(2)).await;
133
134        assert!(sleep.as_mut().now_or_never().is_none());
135
136        tokio::time::advance(std::time::Duration::from_secs(4)).await;
137
138        assert!(matches!(sleep.as_mut().now_or_never(), Some(Ok(()))));
139    }
140
141    #[tokio::test(start_paused = true)]
142    async fn sleep_max_test() {
143        let mut sleep = pin!(Time::sleep(Duration::MAX));
144
145        assert!(sleep.as_mut().now_or_never().is_none());
146
147        tokio::time::advance(std::time::Duration::from_secs(2)).await;
148
149        assert!(sleep.as_mut().now_or_never().is_none());
150
151        tokio::time::advance(std::time::Duration::from_secs(60 * 60 * 24 * 7 * 52 * 20)).await;
152
153        assert!(sleep.as_mut().now_or_never().is_none());
154    }
155
156    #[tokio::test(start_paused = true)]
157    async fn timeout_at_smoke_test() {
158        let mut future = pin!(Time::timeout_at(
159            Time::now() + Duration::from_secs(10),
160            Time::sleep_until(Time::now() + Duration::from_secs(5))
161        ));
162
163        assert!(future.as_mut().now_or_never().is_none());
164
165        tokio::time::advance(std::time::Duration::from_secs(2)).await;
166
167        assert!(future.as_mut().now_or_never().is_none());
168
169        tokio::time::advance(std::time::Duration::from_secs(4)).await;
170
171        assert!(matches!(future.as_mut().now_or_never(), Some(Ok(Ok(())))));
172    }
173
174    #[tokio::test(start_paused = true)]
175    async fn timeout_at_smoke_test_exceeded() {
176        let mut future = pin!(Time::timeout_at(
177            Time::now() + Duration::from_secs(5),
178            std::future::pending::<()>()
179        ));
180
181        assert!(future.as_mut().now_or_never().is_none());
182
183        tokio::time::advance(std::time::Duration::from_secs(2)).await;
184
185        assert!(future.as_mut().now_or_never().is_none());
186
187        tokio::time::advance(std::time::Duration::from_secs(4)).await;
188
189        assert!(matches!(
190            future.as_mut().now_or_never(),
191            Some(Err(Either::Left(Exceeded)))
192        ));
193    }
194
195    #[tokio::test(start_paused = true)]
196    async fn interval_smoke_test() {
197        let mut interval = Time::interval(Duration::from_secs(5));
198
199        assert!(matches!(interval.tick().now_or_never(), Some(Ok(()))));
200
201        assert!(interval.tick().now_or_never().is_none());
202
203        tokio::time::advance(std::time::Duration::from_secs(2)).await;
204
205        assert!(interval.tick().now_or_never().is_none());
206
207        tokio::time::advance(std::time::Duration::from_secs(4)).await;
208
209        assert!(matches!(interval.tick().now_or_never(), Some(Ok(()))));
210
211        assert!(interval.tick().now_or_never().is_none());
212
213        tokio::time::advance(std::time::Duration::from_secs(2)).await;
214
215        assert!(interval.tick().now_or_never().is_none());
216
217        tokio::time::advance(std::time::Duration::from_secs(3)).await;
218
219        assert!(matches!(interval.tick().now_or_never(), Some(Ok(()))));
220    }
221}