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
This commit is contained in:
Max Kellermann 2017-11-14 21:02:53 +01:00
parent 1040b85785
commit febd1ad09c

View File

@ -232,13 +232,17 @@ private:
/** /**
* Wrapper for Activate() which unlocks our mutex. Call this * Wrapper for Activate() which unlocks our mutex. Call this
* if you're holding the mutex. * 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) if (active)
return; return false;
const ScopeUnlock unlock(mutex); const ScopeUnlock unlock(mutex);
Activate(); Activate();
return true;
} }
void ClearRingBuffer() noexcept { 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 /* now that the ring_buffer is full, we can activate
the socket handlers to trigger the first the socket handlers to trigger the first
snd_pcm_writei() */ snd_pcm_writei() */
UnlockActivate(); if (UnlockActivate())
/* since everything may have changed while the
/* check the error again, because a new one may have mutex was unlocked, we need to skip the
been set while our mutex was unlocked in cond.wait() call below and check the new
UnlockActivate() */ status */
if (error) continue;
std::rethrow_exception(error);
/* wait for the DispatchSockets() to make room in the /* wait for the DispatchSockets() to make room in the
ring_buffer */ ring_buffer */