From ca9fcec3643aef9b7cd15cc76248e0981b77decf Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@musicpd.org>
Date: Mon, 8 Jan 2018 09:49:08 +0100
Subject: [PATCH 1/4] thread/Thread: fix indent

---
 src/thread/Thread.hxx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/thread/Thread.hxx b/src/thread/Thread.hxx
index 82c8196d5..f02dbf2e0 100644
--- a/src/thread/Thread.hxx
+++ b/src/thread/Thread.hxx
@@ -62,7 +62,7 @@ public:
 #else
 		return handle != pthread_t();
 #endif
-  }
+	}
 
 	/**
 	 * Check if this thread is the current thread.

From d989dbfec4a16ba8b40209aff7025645c8045681 Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@musicpd.org>
Date: Mon, 8 Jan 2018 09:56:39 +0100
Subject: [PATCH 2/4] thread/Thread: make IsInside() debug-only

This method is only used inside assert().
---
 src/IOThread.cxx      | 4 ++++
 src/IOThread.hxx      | 5 +++++
 src/thread/Thread.hxx | 2 ++
 3 files changed, 11 insertions(+)

diff --git a/src/IOThread.cxx b/src/IOThread.cxx
index 1e3e35074..0a8e0e2ef 100644
--- a/src/IOThread.cxx
+++ b/src/IOThread.cxx
@@ -108,8 +108,12 @@ io_thread_get() noexcept
 	return *io.loop;
 }
 
+#ifndef NDEBUG
+
 bool
 io_thread_inside() noexcept
 {
 	return io.thread.IsInside();
 }
+
+#endif
diff --git a/src/IOThread.hxx b/src/IOThread.hxx
index 63cbbee6e..341c82af7 100644
--- a/src/IOThread.hxx
+++ b/src/IOThread.hxx
@@ -20,6 +20,7 @@
 #ifndef MPD_IO_THREAD_HXX
 #define MPD_IO_THREAD_HXX
 
+#include "check.h"
 #include "Compiler.h"
 
 class EventLoop;
@@ -53,6 +54,8 @@ gcc_const
 EventLoop &
 io_thread_get() noexcept;
 
+#ifndef NDEBUG
+
 /**
  * Is the current thread the I/O thread?
  */
@@ -61,3 +64,5 @@ bool
 io_thread_inside() noexcept;
 
 #endif
+
+#endif
diff --git a/src/thread/Thread.hxx b/src/thread/Thread.hxx
index f02dbf2e0..797ae9676 100644
--- a/src/thread/Thread.hxx
+++ b/src/thread/Thread.hxx
@@ -64,6 +64,7 @@ public:
 #endif
 	}
 
+#ifndef NDEBUG
 	/**
 	 * Check if this thread is the current thread.
 	 */
@@ -81,6 +82,7 @@ public:
 		return pthread_self() == handle;
 #endif
 	}
+#endif
 
 	void Start();
 	void Join();

From 2eef4e67165c38c147980ead785bcf7180b53009 Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@musicpd.org>
Date: Mon, 8 Jan 2018 09:58:18 +0100
Subject: [PATCH 3/4] thread/Thread: add debug attribute "inside_handle"

This attribute shall be used only for IsInside() to make this safe
against a race condition described in #188:

> There is no requirement on the implementation that the ID of the
> created thread be available before the newly created thread starts
> executing.

http://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_create.html):

This means that on some pthread implementations (e.g. Haiku), the
assert(thread.IsInside()) could fail.

Closes #188
---
 NEWS                  |  1 +
 src/thread/Thread.cxx |  4 ++++
 src/thread/Thread.hxx | 12 +++++++++++-
 3 files changed, 16 insertions(+), 1 deletion(-)

diff --git a/NEWS b/NEWS
index 0f61a3aa7..f5182a945 100644
--- a/NEWS
+++ b/NEWS
@@ -1,4 +1,5 @@
 ver 0.20.16 (not yet released)
+* fix crash in debug build on Haiku and other operating systems
 
 ver 0.20.15 (2018/01/05)
 * queue: fix crash after seek failure
diff --git a/src/thread/Thread.cxx b/src/thread/Thread.cxx
index de3c46703..21aec1154 100644
--- a/src/thread/Thread.cxx
+++ b/src/thread/Thread.cxx
@@ -86,6 +86,10 @@ Thread::ThreadProc(void *ctx)
 {
 	Thread &thread = *(Thread *)ctx;
 
+#ifndef NDEBUG
+	thread.inside_handle = pthread_self();
+#endif
+
 	thread.Run();
 
 	return nullptr;
diff --git a/src/thread/Thread.hxx b/src/thread/Thread.hxx
index 797ae9676..45250c5c7 100644
--- a/src/thread/Thread.hxx
+++ b/src/thread/Thread.hxx
@@ -41,6 +41,16 @@ class Thread {
 	DWORD id;
 #else
 	pthread_t handle = pthread_t();
+
+#ifndef NDEBUG
+	/**
+	 * This handle is only used by IsInside(), and is set by the
+	 * thread function.  Since #handle is set by pthread_create()
+	 * which is racy, we need this attribute for early checks
+	 * inside the thread function.
+	 */
+	pthread_t inside_handle = pthread_t();
+#endif
 #endif
 
 public:
@@ -79,7 +89,7 @@ public:
 		   default-constructed values" (comment from
 		   libstdc++) - and if both libstdc++ and libc++ get
 		   away with this, we can do it as well */
-		return pthread_self() == handle;
+		return pthread_self() == inside_handle;
 #endif
 	}
 #endif

From 1f50bdb23075b817774c9ff0d033e7395be13067 Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@musicpd.org>
Date: Fri, 22 Dec 2017 11:04:24 +0100
Subject: [PATCH 4/4] event/Loop: use std::atomic_bool for the "quit" variable

Fixes thread sanitizer warnings.
---
 src/event/Loop.cxx | 6 ++++--
 src/event/Loop.hxx | 3 ++-
 2 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/src/event/Loop.cxx b/src/event/Loop.cxx
index ada820c2d..9e09534da 100644
--- a/src/event/Loop.cxx
+++ b/src/event/Loop.cxx
@@ -27,7 +27,7 @@
 #include <algorithm>
 
 EventLoop::EventLoop()
-	:SocketMonitor(*this)
+	:SocketMonitor(*this), quit(false)
 {
 	SocketMonitor::Open(wake_fd.Get());
 	SocketMonitor::Schedule(SocketMonitor::READ);
@@ -46,7 +46,9 @@ EventLoop::~EventLoop()
 void
 EventLoop::Break()
 {
-	quit = true;
+	if (quit.exchange(true))
+		return;
+
 	wake_fd.Write();
 }
 
diff --git a/src/event/Loop.hxx b/src/event/Loop.hxx
index 1060a8769..cf2ec33b2 100644
--- a/src/event/Loop.hxx
+++ b/src/event/Loop.hxx
@@ -30,6 +30,7 @@
 #include "SocketMonitor.hxx"
 
 #include <chrono>
+#include <atomic>
 #include <list>
 #include <set>
 
@@ -82,7 +83,7 @@ class EventLoop final : SocketMonitor
 
 	std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
 
-	bool quit = false;
+	std::atomic_bool quit;
 
 	/**
 	 * True when the object has been modified and another check is