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)]
29mod tests {
30    use futures::executor::block_on;
31
32    use crate::time::{Duration, Error, Instant, Interval, TimeAbstraction};
33
34    /// A mock implementation for [TimeAbstraction].
35    #[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
36    struct TimeMock<const NOW: u64>;
37
38    impl<const NOW: u64> TimeAbstraction for TimeMock<NOW> {
39        fn now() -> Instant {
40            Instant::MIN + Duration::from_secs(NOW)
41        }
42
43        async fn sleep_until(deadline: Instant) -> Result<(), Error> {
44            if Self::now() < deadline {
45                // Never resolves.
46                core::future::pending::<()>().await;
47            }
48            Ok(())
49        }
50
51        fn interval(_: Duration) -> impl Interval {
52            struct IntervalInternal;
53            impl Interval for IntervalInternal {
54                async fn tick(&mut self) -> Result<(), Error> {
55                    unimplemented!()
56                }
57            }
58            unimplemented!();
59            #[allow(unreachable_code)] // Used for type hinting.
60            IntervalInternal
61        }
62    }
63
64    #[test]
65    fn timeout_with_future_that_completes_in_time_should_not_fail() {
66        async fn should_complete_on_time() {}
67
68        let result = block_on(TimeMock::<0>::timeout_at(
69            Instant::MIN + Duration::from_secs(123),
70            should_complete_on_time(),
71        ));
72        assert!(result.is_ok(), "the future did complete out of time");
73    }
74
75    #[test]
76    fn timeout_with_future_that_completes_out_of_time_should_fail() {
77        async fn should_complete_out_of_time() {}
78
79        let result = block_on(TimeMock::<123>::timeout_at(
80            Instant::MIN + Duration::from_secs(0),
81            should_complete_out_of_time(),
82        ));
83        assert!(result.is_err(), "the future did complete in time");
84    }
85}