event/Loop: add flag `alive`

This replaces the old `dead` flag which was unreliable; it was `false`
if the EventThread was not yet started, which could cause deadlocks in
BlockingCall().
This commit is contained in:
Max Kellermann 2019-02-05 22:38:45 +01:00
parent 261a816b21
commit 6c67408944
4 changed files with 28 additions and 12 deletions

View File

@ -80,7 +80,7 @@ private:
void void
BlockingCall(EventLoop &loop, std::function<void()> &&f) BlockingCall(EventLoop &loop, std::function<void()> &&f)
{ {
if (loop.IsDead() || loop.IsInside()) { if (!loop.IsAlive() || loop.IsInside()) {
/* we're already inside the loop - we can simply call /* we're already inside the loop - we can simply call
the function */ the function */
f(); f();

View File

@ -25,7 +25,13 @@
EventLoop::EventLoop(ThreadId _thread) EventLoop::EventLoop(ThreadId _thread)
:SocketMonitor(*this), :SocketMonitor(*this),
quit(false), dead(false), /* if this instance is hosted by an EventThread (no ThreadId
known yet) then we're not yet alive until the thread is
started; for the main EventLoop instance, we assume it's
already alive, because nobody but EventThread will call
SetAlive() */
alive(!_thread.IsNull()),
quit(false),
thread(_thread) thread(_thread)
{ {
SocketMonitor::Open(SocketDescriptor(wake_fd.Get())); SocketMonitor::Open(SocketDescriptor(wake_fd.Get()));
@ -143,12 +149,11 @@ EventLoop::Run() noexcept
assert(IsInside()); assert(IsInside());
assert(!quit); assert(!quit);
assert(!dead); assert(alive);
assert(busy); assert(busy);
SocketMonitor::Schedule(SocketMonitor::READ); SocketMonitor::Schedule(SocketMonitor::READ);
AtScopeExit(this) { AtScopeExit(this) {
dead = true;
SocketMonitor::Cancel(); SocketMonitor::Cancel();
}; };
@ -215,7 +220,6 @@ EventLoop::Run() noexcept
} while (!quit); } while (!quit);
#ifndef NDEBUG #ifndef NDEBUG
assert(!dead);
assert(busy); assert(busy);
assert(thread.IsInside()); assert(thread.IsInside());
#endif #endif

View File

@ -85,12 +85,15 @@ class EventLoop final : SocketMonitor
std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now(); std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
std::atomic_bool quit;
/** /**
* If this is true, then Run() has returned. * Is this #EventLoop alive, i.e. can events be scheduled?
* This is used by BlockingCall() to determine whether
* schedule in the #EventThread or to call directly (if
* there's no #EventThread yet/anymore).
*/ */
std::atomic_bool dead; bool alive;
std::atomic_bool quit;
/** /**
* True when the object has been modified and another check is * True when the object has been modified and another check is
@ -207,9 +210,12 @@ private:
bool OnSocketReady(unsigned flags) noexcept override; bool OnSocketReady(unsigned flags) noexcept override;
public: public:
gcc_pure void SetAlive(bool _alive) noexcept {
bool IsDead() const noexcept { alive = _alive;
return dead; }
bool IsAlive() const noexcept {
return alive;
} }
/** /**

View File

@ -26,8 +26,11 @@
void void
EventThread::Start() EventThread::Start()
{ {
assert(!event_loop.IsAlive());
assert(!thread.IsDefined()); assert(!thread.IsDefined());
event_loop.SetAlive(true);
thread.Start(); thread.Start();
} }
@ -35,6 +38,9 @@ void
EventThread::Stop() noexcept EventThread::Stop() noexcept
{ {
if (thread.IsDefined()) { if (thread.IsDefined()) {
assert(event_loop.IsAlive());
event_loop.SetAlive(false);
event_loop.Break(); event_loop.Break();
thread.Join(); thread.Join();
} }