diff --git a/src/event/Loop.cxx b/src/event/Loop.cxx index cfb5b6d3e..729aa0c41 100644 --- a/src/event/Loop.cxx +++ b/src/event/Loop.cxx @@ -145,7 +145,7 @@ EventLoop::AbandonFD(SocketEvent &event) noexcept void EventLoop::Insert(CoarseTimerEvent &t) noexcept { - coarse_timers.Insert(t); + coarse_timers.Insert(t, SteadyNow()); again = true; } diff --git a/src/event/TimerWheel.cxx b/src/event/TimerWheel.cxx index 2f406a302..6a2c2f826 100644 --- a/src/event/TimerWheel.cxx +++ b/src/event/TimerWheel.cxx @@ -46,14 +46,18 @@ TimerWheel::TimerWheel() noexcept TimerWheel::~TimerWheel() noexcept = default; void -TimerWheel::Insert(CoarseTimerEvent &t) noexcept +TimerWheel::Insert(CoarseTimerEvent &t, + Event::TimePoint now) noexcept { - /* if this timer's due time is already in the past, don't - insert it into an older bucket because Run() won't look at - it in this iteration */ - const auto due = std::max(t.GetDue(), last_time); + assert(now >= last_time); - buckets[BucketIndexAt(due)].push_back(t); + auto &list = t.GetDue() > now + ? buckets[BucketIndexAt(t.GetDue())] + /* if this timer is already due, insert it into the + "ready" list to be invoked without delay */ + : ready; + + list.push_back(t); empty = false; } @@ -101,6 +105,10 @@ TimerWheel::GetNextDue(const std::size_t bucket_index, inline Event::Duration TimerWheel::GetSleep(Event::TimePoint now) const noexcept { + /* note: not checking the "ready" list here because this + method gets called only from Run() after the "ready" list + has been processed already */ + if (empty) return Event::Duration(-1); @@ -117,6 +125,11 @@ TimerWheel::GetSleep(Event::TimePoint now) const noexcept Event::Duration TimerWheel::Run(const Event::TimePoint now) noexcept { + /* invoke the "ready" list unconditionally */ + ready.clear_and_dispose([&](auto *t){ + t->Run(); + }); + /* check all buckets between the last time we were invoked and now */ const std::size_t start_bucket = BucketIndexAt(last_time); diff --git a/src/event/TimerWheel.hxx b/src/event/TimerWheel.hxx index fd10ee9e4..8579fc3bf 100644 --- a/src/event/TimerWheel.hxx +++ b/src/event/TimerWheel.hxx @@ -64,6 +64,13 @@ class TimerWheel final { */ std::array buckets; + /** + * A list of timers which are already ready. This can happen + * if they are scheduled with a zero duration or scheduled in + * the past. + */ + List ready; + /** * The last time Run() was invoked. This is needed to * determine the range of buckets to be checked, because we @@ -87,13 +94,15 @@ public: ~TimerWheel() noexcept; bool IsEmpty() const noexcept { - return std::all_of(buckets.begin(), buckets.end(), - [](const auto &list){ - return list.empty(); - }); + return ready.empty() && + std::all_of(buckets.begin(), buckets.end(), + [](const auto &list){ + return list.empty(); + }); } - void Insert(CoarseTimerEvent &t) noexcept; + void Insert(CoarseTimerEvent &t, + Event::TimePoint now) noexcept; /** * Invoke all expired #CoarseTimerEvent instances and return