Redo timer math for spurrious events

The timer event sometimes comes late and causes a bunch of issues.  Do
all the expired timer math ourselves instead.

Change-Id: Ie6b438140dc16e1eb9aede3b4f0265f3ae725343
diff --git a/aos/events/event_loop.h b/aos/events/event_loop.h
index 6047cbc..d0ca196 100644
--- a/aos/events/event_loop.h
+++ b/aos/events/event_loop.h
@@ -344,8 +344,9 @@
  protected:
   TimerHandler(EventLoop *event_loop, std::function<void()> fn);
 
-  void Call(std::function<monotonic_clock::time_point()> get_time,
-            monotonic_clock::time_point event_time);
+  monotonic_clock::time_point Call(
+      std::function<monotonic_clock::time_point()> get_time,
+      monotonic_clock::time_point event_time);
 
  private:
   friend class EventLoop;
diff --git a/aos/events/event_loop_tmpl.h b/aos/events/event_loop_tmpl.h
index dfb5a6d..1daf88c 100644
--- a/aos/events/event_loop_tmpl.h
+++ b/aos/events/event_loop_tmpl.h
@@ -100,7 +100,7 @@
   return false;
 }
 
-inline void TimerHandler::Call(
+inline monotonic_clock::time_point TimerHandler::Call(
     std::function<monotonic_clock::time_point()> get_time,
     monotonic_clock::time_point event_time) {
   CHECK_NOTNULL(timing_.timer);
@@ -131,6 +131,7 @@
           monotonic_end_time - monotonic_start_time)
           .count();
   timing_.handler_time.Add(handler_latency);
+  return monotonic_start_time;
 }
 
 inline void PhasedLoopHandler::Call(
diff --git a/aos/events/shm_event_loop.cc b/aos/events/shm_event_loop.cc
index b3026fa..dad62cd 100644
--- a/aos/events/shm_event_loop.cc
+++ b/aos/events/shm_event_loop.cc
@@ -470,8 +470,14 @@
       : TimerHandler(shm_event_loop, std::move(fn)),
         shm_event_loop_(shm_event_loop),
         event_(this) {
-    shm_event_loop_->epoll_.OnReadable(
-        timerfd_.fd(), [this]() { shm_event_loop_->HandleEvent(); });
+    shm_event_loop_->epoll_.OnReadable(timerfd_.fd(), [this]() {
+      // The timer may fire spurriously.  HandleEvent on the event loop will
+      // call the callback if it is needed.  It may also have called it when
+      // processing some other event, and the kernel decided to deliver this
+      // wakeup anyways.
+      timerfd_.Read();
+      shm_event_loop_->HandleEvent();
+    });
   }
 
   ~TimerHandlerState() {
@@ -480,22 +486,29 @@
   }
 
   void HandleEvent() {
-    uint64_t elapsed_cycles = timerfd_.Read();
-    if (elapsed_cycles == 0u) {
-      // We got called before the timer interrupt could happen, but because we
-      // are checking the time, we got called on time.  Push the timer out by 1
-      // cycle.
-      elapsed_cycles = 1u;
-      timerfd_.SetTime(base_ + repeat_offset_, repeat_offset_);
+    CHECK(!event_.valid());
+    const auto monotonic_now = Call(monotonic_clock::now, base_);
+    if (event_.valid()) {
+      // If someone called Setup inside Call, rescheduling is already taken care
+      // of.  Bail.
+      return;
     }
 
-    Call(monotonic_clock::now, base_);
+    if (repeat_offset_ == chrono::seconds(0)) {
+      timerfd_.Disable();
+    } else {
+      // Compute how many cycles have elapsed and schedule the next iteration
+      // for the next iteration in the future.
+      const int elapsed_cycles =
+          std::max<int>(0, (monotonic_now - base_ + repeat_offset_ -
+                            std::chrono::nanoseconds(1)) /
+                               repeat_offset_);
+      base_ += repeat_offset_ * elapsed_cycles;
 
-    base_ += repeat_offset_ * elapsed_cycles;
-
-    if (repeat_offset_ != chrono::seconds(0)) {
+      // Update the heap and schedule the timerfd wakeup.
       event_.set_event_time(base_);
       shm_event_loop_->AddEvent(&event_);
+      timerfd_.SetTime(base_, chrono::seconds(0));
     }
   }