veecle_osal_api/time/
mod.rs

1//! Abstractions for time-based operations.
2//!
3//! The main purpose of this module is to provide the definition of [`TimeAbstraction`], the trait that has to be
4//! implemented to interact with the underlying operating system when performing time-based operations.
5//! In order to keep the time abstractions as decoupled as possible from the running environment, this module provides
6//! its own [`Instant`] and [`Duration`] types.
7//!
8//! # Example
9//!
10//! Runtime actors using time-based operations should rely on the [`TimeAbstraction`] trait, and never use specific
11//! implementations. It is during the runtime's setup that the concrete implementation for the targeted environment has
12//! to be specified.
13//!
14//! ```rust
15//! use core::convert::Infallible;
16//!
17//! use veecle_osal_api::time::{Duration, TimeAbstraction};
18//! use veecle_osal_std::time::Time;
19//! use veecle_os_runtime::{Reader, Storable, Writer};
20//!
21//! #[derive(Debug, Clone, PartialEq, Eq, Default, Storable)]
22//! pub struct Tick {
23//!     since_epoch: Duration,
24//! }
25//!
26//! #[veecle_os_runtime::actor]
27//! async fn tick_writer<T>(mut writer: Writer<'_, Tick>) -> Infallible
28//! where
29//!     T: TimeAbstraction,
30//! {
31//!     let epoch = T::now();
32//!
33//!     loop {
34//!         let _ = T::sleep_until(T::now() + Duration::from_secs(1)).await;
35//!         writer
36//!             .write(Tick {
37//!                 since_epoch: T::now()
38//!                     .duration_since(epoch)
39//!                     .expect("now should be later than epoch"),
40//!             })
41//!             .await;
42//!     }
43//! }
44//!
45//! #[veecle_os_runtime::actor]
46//! async fn tick_reader(mut reader: Reader<'_, Tick>) -> Infallible {
47//!     loop {
48//!         reader.wait_for_update().await.read(|tick| {
49//!             println!("[READER TASK] Tick received: {tick:?}");
50//! #           // Exit the application to allow doc-tests to complete.
51//! #           std::process::exit(0);
52//!         })
53//!     }
54//! }
55//!
56//! # let mut rt = tokio::runtime::Runtime::new().unwrap();
57//! # rt.block_on(async move {
58//! #
59//! veecle_os_runtime::execute! {
60//!     store: [Tick],
61//!     actors: [
62//!         TickWriter<Time>,
63//!         TickReader,
64//!     ]
65//! }.await;
66//!
67//! unreachable!("the runtime instance does not return");
68//! # })
69//! ```
70
71#![allow(async_fn_in_trait)] // Auto-bounds are not necessary here.
72
73use core::future::IntoFuture;
74
75use futures::future::Either;
76
77mod duration;
78mod instant;
79mod system_time;
80mod timeout;
81
82pub use self::duration::Duration;
83pub use self::instant::Instant;
84pub use self::system_time::{SystemTime, SystemTimeError, SystemTimeSync};
85pub use self::timeout::Exceeded;
86use crate::Error;
87
88/// A stream of periodic ticks, created by [`TimeAbstraction::interval`].
89pub trait Interval {
90    /// Completes when the next period has been reached (unless there is an error).
91    ///
92    /// If the stream consumer falls behind and multiple periods go by between reading from the stream, the stream will
93    /// keep track of the missed periods and instantly yield them until caught up.
94    async fn tick(&mut self) -> Result<(), Error>;
95}
96
97/// `TimeAbstraction` is used to perform time-related operations in a platform-agnostic manner.
98pub trait TimeAbstraction {
99    /// Retrieves the current time.
100    fn now() -> Instant;
101
102    /// Returns a future that resolves successfully at the specified `deadline` (or earlier with an error).
103    async fn sleep_until(deadline: Instant) -> Result<(), Error>;
104
105    /// Returns a future that resolves successfully after the specified `duration` (or earlier with an error).
106    ///
107    /// If the `duration` overflows `Instant`, the method sleeps for an unspecified time.
108    async fn sleep(duration: Duration) -> Result<(), Error> {
109        match Self::now().checked_add(duration) {
110            Some(deadline) => Self::sleep_until(deadline).await,
111            None => Self::sleep_until(Self::now() + Duration::max_no_overflow_alias()).await,
112        }
113    }
114
115    /// Returns a future that will resolve when: the wrapped future resolves, the `deadline` is reached, or there is an
116    /// error.
117    async fn timeout_at<F>(
118        deadline: Instant,
119        future: F,
120    ) -> Result<F::Output, Either<Exceeded, Error>>
121    where
122        Self: Sized,
123        F: IntoFuture,
124    {
125        self::timeout::timeout_at::<Self, _>(deadline, future.into_future()).await
126    }
127
128    /// Returns an [`Interval`] that will yield an item straight away and then once every `period` (unless there is an error).
129    ///
130    /// If the stream consumer falls behind and multiple periods go by between reading from the stream, the stream will
131    /// keep track of the missed periods and instantly yield them until caught up.
132    #[must_use]
133    fn interval(period: Duration) -> impl Interval;
134}