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