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.cc b/aos/events/event_loop_runtime.cc
index 727d806..e3d73c1 100644
--- a/aos/events/event_loop_runtime.cc
+++ b/aos/events/event_loop_runtime.cc
@@ -8,4 +8,22 @@
 OnRunForRust::~OnRunForRust() { --runtime_->child_count_; }
 bool OnRunForRust::is_running() const { return runtime_->is_running(); }
 
+std::unique_ptr<TimerForRust> TimerForRust::Make(EventLoopRuntime *runtime) {
+  auto handler = std::unique_ptr<TimerForRust>(new TimerForRust());
+  TimerForRust *inner = handler.get();
+  handler->timer_ = runtime->event_loop()->AddTimer([inner, runtime] {
+    inner->expired_ = true;
+    runtime->DoPoll();
+  });
+  return handler;
+}
+
+bool TimerForRust::Poll() {
+  if (expired_) {
+    // Reset it for next poll.
+    expired_ = false;
+    return true;
+  }
+  return false;
+}
 }  // namespace aos
diff --git a/aos/events/event_loop_runtime.h b/aos/events/event_loop_runtime.h
index 325560f..7cc551f 100644
--- a/aos/events/event_loop_runtime.h
+++ b/aos/events/event_loop_runtime.h
@@ -6,6 +6,7 @@
 // particularly ergonomic for C++. See the Rust wrapper for detailed
 // documentation.
 
+#include <chrono>
 #include <memory>
 #include <optional>
 
@@ -139,6 +140,41 @@
   EventLoopRuntime *const runtime_;
 };
 
+class TimerForRust {
+ public:
+  static std::unique_ptr<TimerForRust> Make(EventLoopRuntime *runtime);
+
+  TimerForRust(const TimerForRust &) = delete;
+  TimerForRust(TimerForRust &&) = delete;
+
+  TimerForRust &operator=(const TimerForRust &) = delete;
+  TimerForRust &operator=(TimerForRust &&) = delete;
+
+  ~TimerForRust() { timer_->Disable(); }
+
+  void Schedule(int64_t base, int64_t repeat_offset) {
+    timer_->Schedule(
+        monotonic_clock::time_point(std::chrono::nanoseconds(base)),
+        std::chrono::nanoseconds(repeat_offset));
+  }
+
+  void Disable() { timer_->Disable(); }
+
+  bool IsDisabled() const { return timer_->IsDisabled(); }
+
+  void set_name(rust::Str name) { timer_->set_name(RustStrToStringView(name)); }
+  rust::Str name() const { return StringViewToRustStr(timer_->name()); }
+
+  // If true, the timer is expired.
+  bool Poll();
+
+ private:
+  TimerForRust() = default;
+
+  TimerHandler *timer_;
+  bool expired_ = false;
+};
+
 class EventLoopRuntime {
  public:
   EventLoopRuntime(EventLoop *event_loop) : event_loop_(event_loop) {}
@@ -199,8 +235,11 @@
 
   OnRunForRust MakeOnRun() { return OnRunForRust(this); }
 
+  std::unique_ptr<TimerForRust> AddTimer() { return TimerForRust::Make(this); }
+
  private:
   friend class OnRunForRust;
+  friend class TimerForRust;
 
   // Polls the top-level future once. This is what all the callbacks should do.
   void DoPoll() {
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)