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/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));
     }
   }