output/wasapi: implement Cancel() properly

Calling consume_all() is illegal in the producer thread.
This commit is contained in:
Max Kellermann 2021-03-10 20:25:20 +01:00
parent 8a045207a7
commit 1fe0c673bc
1 changed files with 28 additions and 1 deletions

View File

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