event/IdleEvent: make a special case of DeferEvent

This commit is contained in:
Max Kellermann 2020-12-01 17:04:14 +01:00
parent 990f2dc1cf
commit c58aaf545f
7 changed files with 66 additions and 70 deletions

View File

@ -28,3 +28,12 @@ DeferEvent::Schedule() noexcept
assert(IsPending()); assert(IsPending());
} }
void
DeferEvent::ScheduleIdle() noexcept
{
if (!IsPending())
loop.AddIdle(*this);
assert(IsPending());
}

View File

@ -60,6 +60,12 @@ public:
void Schedule() noexcept; 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 { void Cancel() noexcept {
if (IsPending()) if (IsPending())
unlink(); unlink();

View File

@ -97,7 +97,7 @@ FullyBufferedSocket::OnSocketReady(unsigned flags) noexcept
{ {
if (flags & SocketEvent::WRITE) { if (flags & SocketEvent::WRITE) {
assert(!output.empty()); assert(!output.empty());
assert(!idle_event.IsActive()); assert(!idle_event.IsPending());
if (!Flush()) if (!Flush())
return; return;

View File

@ -18,26 +18,3 @@
*/ */
#include "IdleEvent.hxx" #include "IdleEvent.hxx"
#include "Loop.hxx"
#include <cassert>
void
IdleEvent::Schedule() noexcept
{
assert(loop.IsInside());
if (IsActive())
/* already scheduled */
return;
loop.AddIdle(*this);
}
void
IdleEvent::Run() noexcept
{
assert(loop.IsInside());
callback();
}

View File

@ -20,8 +20,7 @@
#ifndef MPD_SOCKET_IDLE_EVENT_HXX #ifndef MPD_SOCKET_IDLE_EVENT_HXX
#define MPD_SOCKET_IDLE_EVENT_HXX #define MPD_SOCKET_IDLE_EVENT_HXX
#include "util/BindMethod.hxx" #include "DeferEvent.hxx"
#include "util/IntrusiveList.hxx"
class EventLoop; class EventLoop;
@ -33,39 +32,30 @@ class EventLoop;
* thread that runs the #EventLoop, except where explicitly documented * thread that runs the #EventLoop, except where explicitly documented
* as thread-safe. * as thread-safe.
*/ */
class IdleEvent final : public AutoUnlinkIntrusiveListHook { class IdleEvent final {
friend class EventLoop; DeferEvent event;
friend class IntrusiveList<IdleEvent>;
EventLoop &loop;
using Callback = BoundMethod<void() noexcept>; using Callback = BoundMethod<void() noexcept>;
const Callback callback;
public: public:
IdleEvent(EventLoop &_loop, Callback _callback) noexcept IdleEvent(EventLoop &_loop, Callback _callback) noexcept
:loop(_loop), callback(_callback) {} :event(_loop, _callback) {}
IdleEvent(const IdleEvent &) = delete;
IdleEvent &operator=(const IdleEvent &) = delete;
auto &GetEventLoop() const noexcept { auto &GetEventLoop() const noexcept {
return loop; return event.GetEventLoop();
} }
bool IsActive() const noexcept { bool IsPending() const noexcept {
return is_linked(); return event.IsPending();
} }
void Schedule() noexcept; void Schedule() noexcept {
event.ScheduleIdle();
}
void Cancel() noexcept { void Cancel() noexcept {
if (IsActive()) event.Cancel();
unlink();
} }
private:
void Run() noexcept;
}; };
#endif #endif

View File

@ -153,15 +153,6 @@ EventLoop::RemoveFD(int fd, SocketEvent &event) noexcept
return poll_backend.Remove(fd); return poll_backend.Remove(fd);
} }
void
EventLoop::AddIdle(IdleEvent &i) noexcept
{
assert(IsInside());
idle.push_back(i);
again = true;
}
void void
EventLoop::AddTimer(TimerEvent &t, Event::Duration d) noexcept EventLoop::AddTimer(TimerEvent &t, Event::Duration d) noexcept
{ {
@ -202,6 +193,13 @@ EventLoop::AddDeferred(DeferEvent &d) noexcept
again = true; again = true;
} }
void
EventLoop::AddIdle(DeferEvent &e) noexcept
{
idle.push_front(e);
again = true;
}
void void
EventLoop::RunDeferred() noexcept 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<class ToDuration, class Rep, class Period> template<class ToDuration, class Rep, class Period>
static constexpr ToDuration static constexpr ToDuration
duration_cast_round_up(std::chrono::duration<Rep, Period> d) noexcept duration_cast_round_up(std::chrono::duration<Rep, Period> d) noexcept
@ -301,16 +312,12 @@ EventLoop::Run() noexcept
RunDeferred(); RunDeferred();
/* invoke idle */ if (RunOneIdle())
/* check for other new events after each
while (!idle.empty()) { "idle" invocation to ensure that the other
IdleEvent &m = idle.front(); "idle" events are really invoked at the
idle.pop_front(); very end */
m.Run(); continue;
if (quit)
return;
}
#ifdef HAVE_THREADED_EVENT_LOOP #ifdef HAVE_THREADED_EVENT_LOOP
/* try to handle DeferEvents without WakeFD /* try to handle DeferEvents without WakeFD

View File

@ -47,7 +47,6 @@ namespace Uring { class Queue; class Manager; }
#endif #endif
class TimerEvent; class TimerEvent;
class IdleEvent;
class DeferEvent; class DeferEvent;
class InjectEvent; class InjectEvent;
@ -58,7 +57,7 @@ class InjectEvent;
* thread that runs it, except where explicitly documented as * thread that runs it, except where explicitly documented as
* thread-safe. * thread-safe.
* *
* @see SocketEvent, MultiSocketMonitor, TimerEvent, IdleEvent * @see SocketEvent, MultiSocketMonitor, TimerEvent, DeferEvent, InjectEvent
*/ */
class EventLoop final class EventLoop final
{ {
@ -83,8 +82,10 @@ class EventLoop final
DeferList defer; DeferList defer;
using IdleList = IntrusiveList<IdleEvent>; /**
IdleList idle; * This is like #defer, but gets invoked when the loop is idle.
*/
DeferList idle;
#ifdef HAVE_THREADED_EVENT_LOOP #ifdef HAVE_THREADED_EVENT_LOOP
Mutex mutex; Mutex mutex;
@ -203,14 +204,13 @@ public:
*/ */
bool AbandonFD(SocketEvent &event) noexcept; bool AbandonFD(SocketEvent &event) noexcept;
void AddIdle(IdleEvent &i) noexcept;
void AddTimer(TimerEvent &t, Event::Duration d) noexcept; void AddTimer(TimerEvent &t, Event::Duration d) noexcept;
/** /**
* Schedule a call to DeferEvent::RunDeferred(). * Schedule a call to DeferEvent::RunDeferred().
*/ */
void AddDeferred(DeferEvent &d) noexcept; void AddDeferred(DeferEvent &d) noexcept;
void AddIdle(DeferEvent &e) noexcept;
#ifdef HAVE_THREADED_EVENT_LOOP #ifdef HAVE_THREADED_EVENT_LOOP
/** /**
@ -238,6 +238,13 @@ public:
private: private:
void RunDeferred() noexcept; void RunDeferred() noexcept;
/**
* Invoke one "idle" #DeferEvent.
*
* @return false if there was no such event
*/
bool RunOneIdle() noexcept;
#ifdef HAVE_THREADED_EVENT_LOOP #ifdef HAVE_THREADED_EVENT_LOOP
/** /**
* Invoke all pending InjectEvents. * Invoke all pending InjectEvents.