veecle_osal_api/time/
timeout.rs

1use core::future::Future;
2use core::pin::pin;
3
4use futures::future::{Either, select};
5
6use super::{Error, Instant, TimeAbstraction};
7
8/// A [`TimeAbstraction::timeout_at`] reached the deadline before the future resolved.
9#[derive(Debug)]
10pub struct Exceeded;
11
12/// Implementation for [`TimeAbstraction::timeout_at`].
13pub async fn timeout_at<T, F>(
14    deadline: Instant,
15    future: F,
16) -> Result<F::Output, Either<Exceeded, Error>>
17where
18    T: TimeAbstraction,
19    F: Future,
20{
21    match select(pin!(T::sleep_until(deadline)), pin!(future)).await {
22        Either::Left((Ok(_), _)) => Err(Either::Left(Exceeded)),
23        Either::Left((Err(error), _)) => Err(Either::Right(error)),
24        Either::Right((output, _)) => Ok(output),
25    }
26}
27
28#[cfg(test)]
29#[cfg_attr(coverage_nightly, coverage(off))]
30mod tests {
31    use futures::executor::block_on;
32
33    use crate::time::{Duration, Error, Instant, Interval, TimeAbstraction};
34
35    /// A mock implementation for [TimeAbstraction].
36    #[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
37    struct TimeMock<const NOW: u64>;
38
39    impl<const NOW: u64> TimeAbstraction for TimeMock<NOW> {
40        fn now() -> Instant {
41            Instant::MIN + Duration::from_secs(NOW)
42        }
43
44        async fn sleep_until(deadline: Instant) -> Result<(), Error> {
45            if Self::now() < deadline {
46                // Never resolves.
47                core::future::pending::<()>().await;
48            }
49            Ok(())
50        }
51
52        fn interval(_: Duration) -> impl Interval {
53            struct IntervalInternal;
54            impl Interval for IntervalInternal {
55                async fn tick(&mut self) -> Result<(), Error> {
56                    unimplemented!()
57                }
58            }
59            unimplemented!();
60            #[allow(unreachable_code)] // Used for type hinting.
61            IntervalInternal
62        }
63    }
64
65    #[test]
66    fn timeout_with_future_that_completes_in_time_should_not_fail() {
67        async fn should_complete_on_time() {}
68
69        let result = block_on(TimeMock::<0>::timeout_at(
70            Instant::MIN + Duration::from_secs(123),
71            should_complete_on_time(),
72        ));
73        assert!(result.is_ok(), "the future did complete out of time");
74    }
75
76    #[test]
77    fn timeout_with_future_that_completes_out_of_time_should_fail() {
78        async fn should_complete_out_of_time() {}
79
80        let result = block_on(TimeMock::<123>::timeout_at(
81            Instant::MIN + Duration::from_secs(0),
82            should_complete_out_of_time(),
83        ));
84        assert!(result.is_err(), "the future did complete in time");
85    }
86}