From febd1ad09caa3e463793ab854cd7d24e44ef8009 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Tue, 14 Nov 2017 21:02:53 +0100 Subject: [PATCH] output/alsa: fix deadlock bug After UnlockActivate() returns, we not only need to check for errors, but also for more room in the ring buffer. If we don't check the ring buffer, it may be drained already, and the cond.wait() call will never finish. Closes #151 --- src/output/plugins/AlsaOutputPlugin.cxx | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/output/plugins/AlsaOutputPlugin.cxx b/src/output/plugins/AlsaOutputPlugin.cxx index 78ba98f3a..347539ccd 100644 --- a/src/output/plugins/AlsaOutputPlugin.cxx +++ b/src/output/plugins/AlsaOutputPlugin.cxx @@ -232,13 +232,17 @@ private: /** * Wrapper for Activate() which unlocks our mutex. Call this * if you're holding the mutex. + * + * @return true if Activate() was called, false if the mutex + * was never unlocked */ - void UnlockActivate() noexcept { + bool UnlockActivate() noexcept { if (active) - return; + return false; const ScopeUnlock unlock(mutex); Activate(); + return true; } void ClearRingBuffer() noexcept { @@ -784,13 +788,12 @@ AlsaOutput::Play(const void *chunk, size_t size) /* now that the ring_buffer is full, we can activate the socket handlers to trigger the first snd_pcm_writei() */ - UnlockActivate(); - - /* check the error again, because a new one may have - been set while our mutex was unlocked in - UnlockActivate() */ - if (error) - std::rethrow_exception(error); + if (UnlockActivate()) + /* since everything may have changed while the + mutex was unlocked, we need to skip the + cond.wait() call below and check the new + status */ + continue; /* wait for the DispatchSockets() to make room in the ring_buffer */