diff --git a/src/event/DeferEvent.cxx b/src/event/DeferEvent.cxx index 518aa0a1a..433b432fa 100644 --- a/src/event/DeferEvent.cxx +++ b/src/event/DeferEvent.cxx @@ -21,3 +21,12 @@ DeferEvent::ScheduleIdle() noexcept assert(IsPending()); } + +void +DeferEvent::ScheduleNext() noexcept +{ + if (!IsPending()) + loop.AddNext(*this); + + assert(IsPending()); +} diff --git a/src/event/DeferEvent.hxx b/src/event/DeferEvent.hxx index fa8432f1c..cc5c1fdf9 100644 --- a/src/event/DeferEvent.hxx +++ b/src/event/DeferEvent.hxx @@ -49,6 +49,15 @@ public: */ void ScheduleIdle() noexcept; + /** + * Schedule this event, but only after the next #EventLoop + * iteration (i.e. after the epoll_wait() call, after handling + * all pending I/O events). This is useful for repeated I/O + * operations that should not occupy the whole #EventLoop, + * starving all other I/O events. + */ + void ScheduleNext() noexcept; + void Cancel() noexcept { if (IsPending()) unlink(); diff --git a/src/event/Loop.cxx b/src/event/Loop.cxx index a73e1e963..6d3cf7c2b 100644 --- a/src/event/Loop.cxx +++ b/src/event/Loop.cxx @@ -45,6 +45,7 @@ EventLoop::~EventLoop() noexcept assert(defer.empty()); assert(idle.empty()); + assert(next.empty()); #ifdef HAVE_THREADED_EVENT_LOOP assert(inject.empty()); #endif @@ -201,6 +202,14 @@ EventLoop::AddIdle(DeferEvent &e) noexcept #endif } +void +EventLoop::AddNext(DeferEvent &e) noexcept +{ + assert(IsInside()); + + next.push_back(e); +} + void EventLoop::RunDeferred() noexcept { @@ -295,7 +304,7 @@ EventLoop::Run() noexcept /* invoke timers */ - const auto timeout = HandleTimers(); + Event::Duration timeout = HandleTimers(); if (quit) break; @@ -331,8 +340,13 @@ EventLoop::Run() noexcept /* wait for new event */ + if (!next.empty()) + timeout = Event::Duration{0}; + Wait(timeout); + idle.splice(std::next(idle.begin()), next); + FlushClockCaches(); #ifdef HAVE_THREADED_EVENT_LOOP diff --git a/src/event/Loop.hxx b/src/event/Loop.hxx index d2a551da5..a904f15de 100644 --- a/src/event/Loop.hxx +++ b/src/event/Loop.hxx @@ -67,6 +67,12 @@ class EventLoop final */ DeferList idle; + /** + * This is like #idle, but gets invoked after the next + * epoll_wait() call. + */ + DeferList next; + #ifdef HAVE_THREADED_EVENT_LOOP Mutex mutex; @@ -226,6 +232,7 @@ public: */ void AddDefer(DeferEvent &e) noexcept; void AddIdle(DeferEvent &e) noexcept; + void AddNext(DeferEvent &e) noexcept; #ifdef HAVE_THREADED_EVENT_LOOP /**