summaryrefslogtreecommitdiff
path: root/src/event
diff options
context:
space:
mode:
authorMax Kellermann <max@musicpd.org>2019-02-05 22:38:45 +0100
committerMax Kellermann <max@musicpd.org>2019-02-05 22:38:45 +0100
commit6c67408944d86e756bbce9e993f7b4847cb11caf (patch)
tree61696bb2dd3662d07f185b10c3d05f1f16113a61 /src/event
parent261a816b21ad58501f2345e1f925e1a541863b94 (diff)
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().
Diffstat (limited to 'src/event')
-rw-r--r--src/event/Call.cxx2
-rw-r--r--src/event/Loop.cxx12
-rw-r--r--src/event/Loop.hxx20
-rw-r--r--src/event/Thread.cxx6
4 files changed, 28 insertions, 12 deletions
diff --git a/src/event/Call.cxx b/src/event/Call.cxx
index b03381da7..218d7c312 100644
--- a/src/event/Call.cxx
+++ b/src/event/Call.cxx
@@ -80,7 +80,7 @@ private:
void
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
the function */
f();
diff --git a/src/event/Loop.cxx b/src/event/Loop.cxx
index e0aae6fc4..644cacc39 100644
--- a/src/event/Loop.cxx
+++ b/src/event/Loop.cxx
@@ -25,7 +25,13 @@
EventLoop::EventLoop(ThreadId _thread)
: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)
{
SocketMonitor::Open(SocketDescriptor(wake_fd.Get()));
@@ -143,12 +149,11 @@ EventLoop::Run() noexcept
assert(IsInside());
assert(!quit);
- assert(!dead);
+ assert(alive);
assert(busy);
SocketMonitor::Schedule(SocketMonitor::READ);
AtScopeExit(this) {
- dead = true;
SocketMonitor::Cancel();
};
@@ -215,7 +220,6 @@ EventLoop::Run() noexcept
} while (!quit);
#ifndef NDEBUG
- assert(!dead);
assert(busy);
assert(thread.IsInside());
#endif
diff --git a/src/event/Loop.hxx b/src/event/Loop.hxx
index 80b479494..0a73f6c57 100644
--- a/src/event/Loop.hxx
+++ b/src/event/Loop.hxx
@@ -85,12 +85,15 @@ class EventLoop final : SocketMonitor
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
@@ -207,9 +210,12 @@ private:
bool OnSocketReady(unsigned flags) noexcept override;
public:
- gcc_pure
- bool IsDead() const noexcept {
- return dead;
+ void SetAlive(bool _alive) noexcept {
+ alive = _alive;
+ }
+
+ bool IsAlive() const noexcept {
+ return alive;
}
/**
diff --git a/src/event/Thread.cxx b/src/event/Thread.cxx
index a54b1e880..0ce5a775d 100644
--- a/src/event/Thread.cxx
+++ b/src/event/Thread.cxx
@@ -26,8 +26,11 @@
void
EventThread::Start()
{
+ assert(!event_loop.IsAlive());
assert(!thread.IsDefined());
+ event_loop.SetAlive(true);
+
thread.Start();
}
@@ -35,6 +38,9 @@ void
EventThread::Stop() noexcept
{
if (thread.IsDefined()) {
+ assert(event_loop.IsAlive());
+ event_loop.SetAlive(false);
+
event_loop.Break();
thread.Join();
}