Add timers for Rust event loops
Change-Id: I2fd9077836fa4d6cc5d525dd369c993099528af1
Signed-off-by: Adam Snaider <adsnaider@gmail.com>
diff --git a/aos/events/event_loop_runtime.rs b/aos/events/event_loop_runtime.rs
index 76c1e2e..35a4225 100644
--- a/aos/events/event_loop_runtime.rs
+++ b/aos/events/event_loop_runtime.rs
@@ -47,6 +47,7 @@
future::Future,
marker::PhantomData,
mem::ManuallyDrop,
+ ops::Add,
panic::{catch_unwind, AssertUnwindSafe},
pin::Pin,
slice,
@@ -84,6 +85,7 @@
generate!("aos::OnRunForRust")
generate!("aos::EventLoopRuntime")
generate!("aos::ExitHandle")
+generate!("aos::TimerForRust")
subclass!("aos::ApplicationFuture", RustApplicationFuture)
@@ -681,6 +683,115 @@
pub fn is_running(&self) -> bool {
self.0.is_running()
}
+
+ /// Returns an unarmed timer.
+ pub fn add_timer(&mut self) -> Timer {
+ Timer(self.0.as_mut().AddTimer())
+ }
+
+ /// Returns a timer that goes off every `duration`-long ticks.
+ pub fn add_interval(&mut self, duration: Duration) -> Timer {
+ let mut timer = self.add_timer();
+ timer.setup(self.monotonic_now(), Some(duration));
+ timer
+ }
+}
+
+/// An event loop primitive that allows sleeping asynchronously.
+///
+/// # Examples
+///
+/// ```no_run
+/// # use aos_events_event_loop_runtime::EventLoopRuntime;
+/// # use std::time::Duration;
+/// # fn compile_check(runtime: &mut EventLoopRuntime<'_>) {
+/// # let mut timer = runtime.add_timer();
+/// // Goes as soon as awaited.
+/// timer.setup(runtime.monotonic_now(), None);
+/// // Goes off once in 2 seconds.
+/// timer.setup(runtime.monotonic_now() + Duration::from_secs(2), None);
+/// // Goes off as soon as awaited and every 2 seconds afterwards.
+/// timer.setup(runtime.monotonic_now(), Some(Duration::from_secs(1)));
+/// async {
+/// for i in 0..10 {
+/// timer.tick().await;
+/// }
+/// // Timer won't off anymore. Next `tick` will never return.
+/// timer.disable();
+/// timer.tick().await;
+/// };
+/// # }
+/// ```
+pub struct Timer(UniquePtr<ffi::aos::TimerForRust>);
+
+/// A "tick" for a [`Timer`].
+///
+/// This is the raw future generated by the [`Timer::tick`] function.
+pub struct TimerTick<'a>(&'a mut Timer);
+
+impl Timer {
+ /// Arms the timer.
+ ///
+ /// The timer should sleep until `base`, `base + repeat`, `base + repeat * 2`, ...
+ /// If `repeat` is `None`, then the timer only expires once at `base`.
+ pub fn setup(&mut self, base: MonotonicInstant, repeat: Option<Duration>) {
+ self.0.pin_mut().Schedule(
+ base.0,
+ repeat
+ .unwrap_or(Duration::from_nanos(0))
+ .as_nanos()
+ .try_into()
+ .expect("Out of range: Internal clock uses 64 bits"),
+ );
+ }
+
+ /// Disarms the timer.
+ ///
+ /// Can be re-enabled by calling `setup` again.
+ pub fn disable(&mut self) {
+ self.0.pin_mut().Disable();
+ }
+
+ /// Returns `true` if the timer is enabled.
+ pub fn is_enabled(&self) -> bool {
+ !self.0.IsDisabled()
+ }
+
+ /// Sets the name of the timer.
+ ///
+ /// This can be useful to get a descriptive name in the timing reports.
+ pub fn set_name(&mut self, name: &str) {
+ self.0.pin_mut().set_name(name);
+ }
+
+ /// Gets the name of the timer.
+ pub fn name(&self) -> &str {
+ self.0.name()
+ }
+
+ /// Returns a tick which can be `.await`ed.
+ ///
+ /// This tick will resolve on the next timer expired.
+ pub fn tick(&mut self) -> TimerTick {
+ TimerTick(self)
+ }
+
+ /// Polls the timer, returning `[Poll::Ready]` only once the timer expired.
+ fn poll(&mut self) -> Poll<()> {
+ if self.0.pin_mut().Poll() {
+ Poll::Ready(())
+ } else {
+ Poll::Pending
+ }
+ }
+}
+
+impl Future for TimerTick<'_> {
+ type Output = ();
+
+ fn poll(mut self: Pin<&mut Self>, _: &mut std::task::Context) -> Poll<()> {
+ self.0.poll()
+ }
}
/// Provides async blocking access to messages on a channel. This will return every message on the
@@ -1342,6 +1453,14 @@
}
}
+impl Add<Duration> for MonotonicInstant {
+ type Output = MonotonicInstant;
+
+ fn add(self, rhs: Duration) -> Self::Output {
+ Self(self.0 + i64::try_from(rhs.as_nanos()).unwrap())
+ }
+}
+
impl fmt::Debug for MonotonicInstant {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.duration_since_epoch().fmt(f)