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());
}
void
DeferEvent::ScheduleIdle() noexcept
{
if (!IsPending())
loop.AddIdle(*this);
assert(IsPending());
}

View File

@ -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();

View File

@ -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;

View File

@ -18,26 +18,3 @@
*/
#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
#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<IdleEvent>;
EventLoop &loop;
class IdleEvent final {
DeferEvent event;
using Callback = BoundMethod<void() noexcept>;
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

View File

@ -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<class ToDuration, class Rep, class Period>
static constexpr ToDuration
duration_cast_round_up(std::chrono::duration<Rep, Period> 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

View File

@ -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<IdleEvent>;
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.