event/TimerWheel: add a "ready" list as a special case
This reduces delays of zero-duration timers from up to 1 second to zero. libavahi-client schedules zero-duration timers often.
This commit is contained in:
parent
0091c4e12b
commit
f5f296b13a
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -64,6 +64,13 @@ class TimerWheel final {
|
||||
*/
|
||||
std::array<List, N_BUCKETS> 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
|
||||
|
Loading…
Reference in New Issue
Block a user