From c58aaf545fbe42ce66ce8e0bc0956428d7e1ff5d Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Tue, 1 Dec 2020 17:04:14 +0100 Subject: [PATCH] event/IdleEvent: make a special case of DeferEvent --- src/event/DeferEvent.cxx | 9 +++++++ src/event/DeferEvent.hxx | 6 +++++ src/event/FullyBufferedSocket.cxx | 2 +- src/event/IdleEvent.cxx | 23 ---------------- src/event/IdleEvent.hxx | 32 ++++++++-------------- src/event/Loop.cxx | 45 ++++++++++++++++++------------- src/event/Loop.hxx | 19 ++++++++----- 7 files changed, 66 insertions(+), 70 deletions(-) diff --git a/src/event/DeferEvent.cxx b/src/event/DeferEvent.cxx index f8bfacb28..aae6c9396 100644 --- a/src/event/DeferEvent.cxx +++ b/src/event/DeferEvent.cxx @@ -28,3 +28,12 @@ DeferEvent::Schedule() noexcept assert(IsPending()); } + +void +DeferEvent::ScheduleIdle() noexcept +{ + if (!IsPending()) + loop.AddIdle(*this); + + assert(IsPending()); +} diff --git a/src/event/DeferEvent.hxx b/src/event/DeferEvent.hxx index f1914fe08..c392f886c 100644 --- a/src/event/DeferEvent.hxx +++ b/src/event/DeferEvent.hxx @@ -60,6 +60,12 @@ public: void Schedule() noexcept; + /** + * Schedule this event, but only after the #EventLoop is idle, + * i.e. before going to sleep. + */ + void ScheduleIdle() noexcept; + void Cancel() noexcept { if (IsPending()) unlink(); diff --git a/src/event/FullyBufferedSocket.cxx b/src/event/FullyBufferedSocket.cxx index d1a1b00d5..7cb771ef6 100644 --- a/src/event/FullyBufferedSocket.cxx +++ b/src/event/FullyBufferedSocket.cxx @@ -97,7 +97,7 @@ FullyBufferedSocket::OnSocketReady(unsigned flags) noexcept { if (flags & SocketEvent::WRITE) { assert(!output.empty()); - assert(!idle_event.IsActive()); + assert(!idle_event.IsPending()); if (!Flush()) return; diff --git a/src/event/IdleEvent.cxx b/src/event/IdleEvent.cxx index 8dc5c72ee..43b8e1b58 100644 --- a/src/event/IdleEvent.cxx +++ b/src/event/IdleEvent.cxx @@ -18,26 +18,3 @@ */ #include "IdleEvent.hxx" -#include "Loop.hxx" - -#include - -void -IdleEvent::Schedule() noexcept -{ - assert(loop.IsInside()); - - if (IsActive()) - /* already scheduled */ - return; - - loop.AddIdle(*this); -} - -void -IdleEvent::Run() noexcept -{ - assert(loop.IsInside()); - - callback(); -} diff --git a/src/event/IdleEvent.hxx b/src/event/IdleEvent.hxx index 17ca3c7e3..76415b62b 100644 --- a/src/event/IdleEvent.hxx +++ b/src/event/IdleEvent.hxx @@ -20,8 +20,7 @@ #ifndef MPD_SOCKET_IDLE_EVENT_HXX #define MPD_SOCKET_IDLE_EVENT_HXX -#include "util/BindMethod.hxx" -#include "util/IntrusiveList.hxx" +#include "DeferEvent.hxx" class EventLoop; @@ -33,39 +32,30 @@ class EventLoop; * thread that runs the #EventLoop, except where explicitly documented * as thread-safe. */ -class IdleEvent final : public AutoUnlinkIntrusiveListHook { - friend class EventLoop; - friend class IntrusiveList; - - EventLoop &loop; +class IdleEvent final { + DeferEvent event; using Callback = BoundMethod; - const Callback callback; public: IdleEvent(EventLoop &_loop, Callback _callback) noexcept - :loop(_loop), callback(_callback) {} - - IdleEvent(const IdleEvent &) = delete; - IdleEvent &operator=(const IdleEvent &) = delete; + :event(_loop, _callback) {} auto &GetEventLoop() const noexcept { - return loop; + return event.GetEventLoop(); } - bool IsActive() const noexcept { - return is_linked(); + bool IsPending() const noexcept { + return event.IsPending(); } - void Schedule() noexcept; + void Schedule() noexcept { + event.ScheduleIdle(); + } void Cancel() noexcept { - if (IsActive()) - unlink(); + event.Cancel(); } - -private: - void Run() noexcept; }; #endif diff --git a/src/event/Loop.cxx b/src/event/Loop.cxx index 2a304f950..9eb76c805 100644 --- a/src/event/Loop.cxx +++ b/src/event/Loop.cxx @@ -153,15 +153,6 @@ EventLoop::RemoveFD(int fd, SocketEvent &event) noexcept return poll_backend.Remove(fd); } -void -EventLoop::AddIdle(IdleEvent &i) noexcept -{ - assert(IsInside()); - - idle.push_back(i); - again = true; -} - void EventLoop::AddTimer(TimerEvent &t, Event::Duration d) noexcept { @@ -202,6 +193,13 @@ EventLoop::AddDeferred(DeferEvent &d) noexcept again = true; } +void +EventLoop::AddIdle(DeferEvent &e) noexcept +{ + idle.push_front(e); + again = true; +} + void EventLoop::RunDeferred() noexcept { @@ -212,6 +210,19 @@ EventLoop::RunDeferred() noexcept } } +bool +EventLoop::RunOneIdle() noexcept +{ + if (idle.empty()) + return false; + + idle.pop_front_and_dispose([](DeferEvent *e){ + e->Run(); + }); + + return true; +} + template static constexpr ToDuration duration_cast_round_up(std::chrono::duration d) noexcept @@ -301,16 +312,12 @@ EventLoop::Run() noexcept RunDeferred(); - /* invoke idle */ - - while (!idle.empty()) { - IdleEvent &m = idle.front(); - idle.pop_front(); - m.Run(); - - if (quit) - return; - } + if (RunOneIdle()) + /* check for other new events after each + "idle" invocation to ensure that the other + "idle" events are really invoked at the + very end */ + continue; #ifdef HAVE_THREADED_EVENT_LOOP /* try to handle DeferEvents without WakeFD diff --git a/src/event/Loop.hxx b/src/event/Loop.hxx index 0d9868f10..6d5dc1618 100644 --- a/src/event/Loop.hxx +++ b/src/event/Loop.hxx @@ -47,7 +47,6 @@ namespace Uring { class Queue; class Manager; } #endif class TimerEvent; -class IdleEvent; class DeferEvent; class InjectEvent; @@ -58,7 +57,7 @@ class InjectEvent; * thread that runs it, except where explicitly documented as * thread-safe. * - * @see SocketEvent, MultiSocketMonitor, TimerEvent, IdleEvent + * @see SocketEvent, MultiSocketMonitor, TimerEvent, DeferEvent, InjectEvent */ class EventLoop final { @@ -83,8 +82,10 @@ class EventLoop final DeferList defer; - using IdleList = IntrusiveList; - IdleList idle; + /** + * This is like #defer, but gets invoked when the loop is idle. + */ + DeferList idle; #ifdef HAVE_THREADED_EVENT_LOOP Mutex mutex; @@ -203,14 +204,13 @@ public: */ bool AbandonFD(SocketEvent &event) noexcept; - void AddIdle(IdleEvent &i) noexcept; - void AddTimer(TimerEvent &t, Event::Duration d) noexcept; /** * Schedule a call to DeferEvent::RunDeferred(). */ void AddDeferred(DeferEvent &d) noexcept; + void AddIdle(DeferEvent &e) noexcept; #ifdef HAVE_THREADED_EVENT_LOOP /** @@ -238,6 +238,13 @@ public: private: void RunDeferred() noexcept; + /** + * Invoke one "idle" #DeferEvent. + * + * @return false if there was no such event + */ + bool RunOneIdle() noexcept; + #ifdef HAVE_THREADED_EVENT_LOOP /** * Invoke all pending InjectEvents.