From 1fe0c673bc3ead4e99e47e61e7aa3f0ed590bbba Mon Sep 17 00:00:00 2001 From: Max Kellermann <max@musicpd.org> Date: Wed, 10 Mar 2021 20:25:20 +0100 Subject: [PATCH] output/wasapi: implement Cancel() properly Calling consume_all() is illegal in the producer thread. --- .../plugins/wasapi/WasapiOutputPlugin.cxx | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/output/plugins/wasapi/WasapiOutputPlugin.cxx b/src/output/plugins/wasapi/WasapiOutputPlugin.cxx index ba6b61a80..e7075c0b5 100644 --- a/src/output/plugins/wasapi/WasapiOutputPlugin.cxx +++ b/src/output/plugins/wasapi/WasapiOutputPlugin.cxx @@ -170,7 +170,10 @@ class WasapiOutputThread { bool started = false; + std::atomic_bool cancel = false; + enum class Status : uint32_t { FINISH, PLAY, PAUSE }; + alignas(BOOST_LOCKFREE_CACHELINE_BYTES) std::atomic<Status> status = Status::PAUSE; alignas(BOOST_LOCKFREE_CACHELINE_BYTES) struct { @@ -201,6 +204,24 @@ public: void Play() noexcept { return SetStatus(Status::PLAY); } void Pause() noexcept { return SetStatus(Status::PAUSE); } + /** + * Instruct the thread to discard the buffer (and wait for + * completion). This needs to be done inside this thread, + * because only the consumer thread is allowed to do that. + */ + void Cancel() noexcept { + cancel.store(true); + event.Set(); + + while (cancel.load() && !error.occur.load()) + Wait(); + + /* not rethrowing the exception here via + CheckException() because this method must be + "noexcept"; the next WasapiOutput::Play() call will + throw */ + } + /** * Wait for the thread to finish some work (e.g. until some * buffer space becomes available). @@ -366,6 +387,12 @@ try { while (true) { event.Wait(); + if (cancel.load()) { + spsc_buffer.consume_all([](auto &&) {}); + cancel.store(false); + InterruptWaiter(); + } + Status current_state = status.load(); switch (current_state) { case Status::FINISH: @@ -707,7 +734,7 @@ WasapiOutput::Cancel() noexcept { assert(thread); - thread->spsc_buffer.consume_all([](auto &&) {}); + thread->Cancel(); } /// run inside COMWorkerThread