Add AddPhasedLoop to support phased loops

This lets us convert phased loops over nicely in various places.

Change-Id: Icdde4520f991fc541fbbe7ab3d9b945bb8c12e83
diff --git a/aos/events/shm-event-loop.cc b/aos/events/shm-event-loop.cc
index 08f9e1f..8c0f3a2 100644
--- a/aos/events/shm-event-loop.cc
+++ b/aos/events/shm-event-loop.cc
@@ -10,6 +10,7 @@
 #include "aos/init.h"
 #include "aos/logging/logging.h"
 #include "aos/queue.h"
+#include "aos/util/phased_loop.h"
 
 namespace aos {
 
@@ -244,6 +245,8 @@
 };
 
 // Adapter class to adapt a timerfd to a TimerHandler.
+// The part of the API which is accessed by the TimerHandler interface needs to
+// be threadsafe.  This means Setup and Disable.
 class TimerHandlerState : public TimerHandler {
  public:
   TimerHandlerState(ShmEventLoop *shm_event_loop, ::std::function<void()> fn)
@@ -259,10 +262,14 @@
 
   void Setup(monotonic_clock::time_point base,
              monotonic_clock::duration repeat_offset) override {
+    // SetTime is threadsafe already.
     timerfd_.SetTime(base, repeat_offset);
   }
 
-  void Disable() override { timerfd_.Disable(); }
+  void Disable() override {
+    // Disable is also threadsafe already.
+    timerfd_.Disable();
+  }
 
  private:
   ShmEventLoop *shm_event_loop_;
@@ -272,6 +279,70 @@
   // Function to be run on the thread
   ::std::function<void()> fn_;
 };
+
+// Adapter class to the timerfd and PhasedLoop.
+// The part of the API which is accessed by the PhasedLoopHandler interface
+// needs to be threadsafe.  This means set_interval_and_offset
+class PhasedLoopHandler : public ::aos::PhasedLoopHandler {
+ public:
+  PhasedLoopHandler(ShmEventLoop *shm_event_loop, ::std::function<void(int)> fn,
+                    const monotonic_clock::duration interval,
+                    const monotonic_clock::duration offset)
+      : shm_event_loop_(shm_event_loop),
+        phased_loop_(interval, shm_event_loop_->monotonic_now(), offset),
+        fn_(::std::move(fn)) {
+    shm_event_loop_->epoll_.OnReadable(timerfd_.fd(), [this]() {
+      MutexLocker locker(&shm_event_loop_->thread_state_.mutex_);
+      {
+        MutexLocker locker(&mutex_);
+        timerfd_.Read();
+      }
+      // Call the function.  To avoid needing a recursive mutex, drop the lock
+      // before running the function.
+      fn_(cycles_elapsed_);
+      {
+        MutexLocker locker(&mutex_);
+        Reschedule();
+      }
+    });
+  }
+
+  ~PhasedLoopHandler() { shm_event_loop_->epoll_.DeleteFd(timerfd_.fd()); }
+
+  void set_interval_and_offset(
+      const monotonic_clock::duration interval,
+      const monotonic_clock::duration offset) override {
+    MutexLocker locker(&mutex_);
+    phased_loop_.set_interval_and_offset(interval, offset);
+  }
+
+  void Startup() {
+    MutexLocker locker(&mutex_);
+    phased_loop_.Reset(shm_event_loop_->monotonic_now());
+    Reschedule();
+  }
+
+ private:
+  // Reschedules the timer.  Must be called with the mutex held.
+  void Reschedule() {
+    cycles_elapsed_ = phased_loop_.Iterate(shm_event_loop_->monotonic_now());
+    timerfd_.SetTime(phased_loop_.sleep_time(), ::aos::monotonic_clock::zero());
+  }
+
+  ShmEventLoop *shm_event_loop_;
+
+  // Mutex to protect access to the timerfd_ (not strictly necessary), and the
+  // phased_loop (necessary).
+  ::aos::Mutex mutex_;
+
+  TimerFd timerfd_;
+  time::PhasedLoop phased_loop_;
+
+  int cycles_elapsed_ = 1;
+
+  // Function to be run
+  const ::std::function<void(int)> fn_;
+};
 }  // namespace internal
 
 ::std::unique_ptr<RawFetcher> ShmEventLoop::MakeRawFetcher(
@@ -308,6 +379,19 @@
   return timers_.back().get();
 }
 
+PhasedLoopHandler *ShmEventLoop::AddPhasedLoop(
+    ::std::function<void(int)> callback,
+    const monotonic_clock::duration interval,
+    const monotonic_clock::duration offset) {
+  ::std::unique_ptr<internal::PhasedLoopHandler> phased_loop(
+      new internal::PhasedLoopHandler(this, ::std::move(callback), interval,
+                                      offset));
+
+  phased_loops_.push_back(::std::move(phased_loop));
+
+  return phased_loops_.back().get();
+}
+
 void ShmEventLoop::OnRun(::std::function<void()> on_run) {
   on_run_.push_back(::std::move(on_run));
 }
@@ -332,6 +416,12 @@
   for (const auto &run : on_run_) {
     run();
   }
+
+  // Start up all the phased loops.
+  for (::std::unique_ptr<internal::PhasedLoopHandler> &phased_loop :
+       phased_loops_) {
+    phased_loop->Startup();
+  }
   // TODO(austin): We don't need a separate watcher thread if there are only
   // watchers and fetchers.  Could lazily create the epoll loop and pick a
   // victim watcher to run in this thread.