DecoderControl: convert mutex and client_cond to a reference

Share the Mutex between the DecoderThread and the PlayerThread.  This
simplifies synchronization between the two threads and fixes a freeze
problem: while the PlayerThread waits for the DeocderThread, it cannot
answer requests from the main thread, and the main thread will block
until the DecoderThread finishes.
This commit is contained in:
Max Kellermann 2013-10-28 10:09:04 +01:00
parent 5b5675cc12
commit 1ad2475f9e
4 changed files with 30 additions and 24 deletions

1
NEWS
View File

@ -34,6 +34,7 @@ ver 0.18 (2012/??/??)
- mvp: remove obsolete plugin
* improved decoder/output error reporting
* eliminate timer wakeup on idle MPD
* fix unresponsive MPD while waiting for stream
ver 0.17.6 (2013/10/14)
* mixer:

View File

@ -26,8 +26,9 @@
#include <assert.h>
DecoderControl::DecoderControl()
:state(DecoderState::STOP),
DecoderControl::DecoderControl(Mutex &_mutex, Cond &_client_cond)
:mutex(_mutex), client_cond(_client_cond),
state(DecoderState::STOP),
command(DecoderCommand::NONE),
song(nullptr),
replay_gain_db(0), replay_gain_prev_db(0) {}

View File

@ -62,8 +62,13 @@ struct DecoderControl {
/**
* This lock protects #state and #command.
*
* This is usually a reference to PlayerControl::mutex, so
* that both player thread and decoder thread share a mutex.
* This simplifies synchronization with #cond and
* #client_cond.
*/
mutable Mutex mutex;
Mutex &mutex;
/**
* Trigger this object after you have modified #command. This
@ -75,8 +80,10 @@ struct DecoderControl {
/**
* The trigger of this object's client. It is signalled
* whenever an event occurs.
*
* This is usually a reference to PlayerControl::cond.
*/
Cond client_cond;
Cond &client_cond;
DecoderState state;
DecoderCommand command;
@ -143,7 +150,11 @@ struct DecoderControl {
MixRampInfo mix_ramp, previous_mix_ramp;
DecoderControl();
/**
* @param _mutex see #mutex
* @param _client_cond see #client_cond
*/
DecoderControl(Mutex &_mutex, Cond &_client_cond);
~DecoderControl();
/**

View File

@ -322,9 +322,9 @@ Player::WaitForDecoder()
queued = false;
Error error = dc.LockGetError();
pc.Lock();
Error error = dc.GetError();
if (error.IsDefined()) {
pc.Lock();
pc.SetError(PlayerError::DECODER, std::move(error));
pc.next_song->Free();
@ -347,8 +347,6 @@ Player::WaitForDecoder()
player_check_decoder_startup() */
decoder_starting = true;
pc.Lock();
/* update PlayerControl's song information */
pc.total_time = pc.next_song->GetDuration();
pc.bit_rate = 0;
@ -429,14 +427,11 @@ Player::CheckDecoderStartup()
{
assert(decoder_starting);
dc.Lock();
pc.Lock();
Error error = dc.GetError();
if (error.IsDefined()) {
/* the decoder failed */
dc.Unlock();
pc.Lock();
pc.SetError(PlayerError::DECODER, std::move(error));
pc.Unlock();
@ -444,7 +439,7 @@ Player::CheckDecoderStartup()
} else if (!dc.IsStarting()) {
/* the decoder is ready and ok */
dc.Unlock();
pc.Unlock();
if (output_open &&
!audio_output_all_wait(pc, 1))
@ -475,7 +470,7 @@ Player::CheckDecoderStartup()
/* the decoder is not yet ready; wait
some more */
dc.WaitForDecoder();
dc.Unlock();
pc.Unlock();
return true;
}
@ -807,19 +802,19 @@ Player::PlayNextChunk()
} else {
/* there are not enough decoded chunks yet */
dc.Lock();
pc.Lock();
if (dc.IsIdle()) {
/* the decoder isn't running, abort
cross fading */
dc.Unlock();
pc.Unlock();
xfade_state = CrossFadeState::DISABLED;
} else {
/* wait for the decoder */
dc.Signal();
dc.WaitForDecoder();
dc.Unlock();
pc.Unlock();
return true;
}
@ -865,12 +860,12 @@ Player::PlayNextChunk()
/* this formula should prevent that the decoder gets woken up
with each chunk; it is more efficient to make it decode a
larger block at a time */
dc.Lock();
pc.Lock();
if (!dc.IsIdle() &&
dc.pipe->GetSize() <= (pc.buffered_before_play +
buffer.GetSize() * 3) / 4)
dc.Signal();
dc.Unlock();
pc.Unlock();
return true;
}
@ -957,11 +952,9 @@ Player::Run()
!SendSilence())
break;
dc.Lock();
pc.Lock();
/* XXX race condition: check decoder again */
dc.WaitForDecoder();
dc.Unlock();
pc.Lock();
continue;
} else {
/* buffering is complete */
@ -1107,7 +1100,7 @@ player_task(void *arg)
{
PlayerControl &pc = *(PlayerControl *)arg;
DecoderControl dc;
DecoderControl dc(pc.mutex, pc.cond);
decoder_thread_start(dc);
MusicBuffer buffer(pc.buffer_chunks);