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
|
void
|
||||||
EventLoop::Insert(CoarseTimerEvent &t) noexcept
|
EventLoop::Insert(CoarseTimerEvent &t) noexcept
|
||||||
{
|
{
|
||||||
coarse_timers.Insert(t);
|
coarse_timers.Insert(t, SteadyNow());
|
||||||
again = true;
|
again = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,14 +46,18 @@ TimerWheel::TimerWheel() noexcept
|
|||||||
TimerWheel::~TimerWheel() noexcept = default;
|
TimerWheel::~TimerWheel() noexcept = default;
|
||||||
|
|
||||||
void
|
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
|
assert(now >= last_time);
|
||||||
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);
|
|
||||||
|
|
||||||
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;
|
empty = false;
|
||||||
}
|
}
|
||||||
@ -101,6 +105,10 @@ TimerWheel::GetNextDue(const std::size_t bucket_index,
|
|||||||
inline Event::Duration
|
inline Event::Duration
|
||||||
TimerWheel::GetSleep(Event::TimePoint now) const noexcept
|
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)
|
if (empty)
|
||||||
return Event::Duration(-1);
|
return Event::Duration(-1);
|
||||||
|
|
||||||
@ -117,6 +125,11 @@ TimerWheel::GetSleep(Event::TimePoint now) const noexcept
|
|||||||
Event::Duration
|
Event::Duration
|
||||||
TimerWheel::Run(const Event::TimePoint now) noexcept
|
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
|
/* check all buckets between the last time we were invoked and
|
||||||
now */
|
now */
|
||||||
const std::size_t start_bucket = BucketIndexAt(last_time);
|
const std::size_t start_bucket = BucketIndexAt(last_time);
|
||||||
|
@ -64,6 +64,13 @@ class TimerWheel final {
|
|||||||
*/
|
*/
|
||||||
std::array<List, N_BUCKETS> buckets;
|
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
|
* The last time Run() was invoked. This is needed to
|
||||||
* determine the range of buckets to be checked, because we
|
* determine the range of buckets to be checked, because we
|
||||||
@ -87,13 +94,15 @@ public:
|
|||||||
~TimerWheel() noexcept;
|
~TimerWheel() noexcept;
|
||||||
|
|
||||||
bool IsEmpty() const noexcept {
|
bool IsEmpty() const noexcept {
|
||||||
return std::all_of(buckets.begin(), buckets.end(),
|
return ready.empty() &&
|
||||||
[](const auto &list){
|
std::all_of(buckets.begin(), buckets.end(),
|
||||||
return list.empty();
|
[](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
|
* Invoke all expired #CoarseTimerEvent instances and return
|
||||||
|
Loading…
Reference in New Issue
Block a user