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}