output: protect audio_output.open with the mutex
There was a deadlock between the output thread and the player thread: when the output thread failed (and closed itself) while the player thread worked with the audio_output object, MPD could crash.
This commit is contained in:
parent
71cd24954a
commit
4dbf73d88b
@ -169,6 +169,19 @@ static void audio_output_wait_all(void)
|
|||||||
notify_wait(&audio_output_client_notify);
|
notify_wait(&audio_output_client_notify);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
audio_output_reset_reopen(struct audio_output *ao)
|
||||||
|
{
|
||||||
|
g_mutex_lock(ao->mutex);
|
||||||
|
|
||||||
|
if (!ao->open && ao->fail_timer != NULL) {
|
||||||
|
g_timer_destroy(ao->fail_timer);
|
||||||
|
ao->fail_timer = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_mutex_unlock(ao->mutex);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resets the "reopen" flag on all audio devices. MPD should
|
* Resets the "reopen" flag on all audio devices. MPD should
|
||||||
* immediately retry to open the device instead of waiting for the
|
* immediately retry to open the device instead of waiting for the
|
||||||
@ -180,10 +193,7 @@ audio_output_all_reset_reopen(void)
|
|||||||
for (unsigned i = 0; i < num_audio_outputs; ++i) {
|
for (unsigned i = 0; i < num_audio_outputs; ++i) {
|
||||||
struct audio_output *ao = &audio_outputs[i];
|
struct audio_output *ao = &audio_outputs[i];
|
||||||
|
|
||||||
if (!ao->open && ao->fail_timer != NULL) {
|
audio_output_reset_reopen(ao);
|
||||||
g_timer_destroy(ao->fail_timer);
|
|
||||||
ao->fail_timer = NULL;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -289,6 +299,9 @@ static bool
|
|||||||
chunk_is_consumed_in(const struct audio_output *ao,
|
chunk_is_consumed_in(const struct audio_output *ao,
|
||||||
const struct music_chunk *chunk)
|
const struct music_chunk *chunk)
|
||||||
{
|
{
|
||||||
|
if (!ao->open)
|
||||||
|
return true;
|
||||||
|
|
||||||
if (ao->chunk == NULL)
|
if (ao->chunk == NULL)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -312,9 +325,6 @@ chunk_is_consumed(const struct music_chunk *chunk)
|
|||||||
const struct audio_output *ao = &audio_outputs[i];
|
const struct audio_output *ao = &audio_outputs[i];
|
||||||
bool consumed;
|
bool consumed;
|
||||||
|
|
||||||
if (!ao->open)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
g_mutex_lock(ao->mutex);
|
g_mutex_lock(ao->mutex);
|
||||||
consumed = chunk_is_consumed_in(ao, chunk);
|
consumed = chunk_is_consumed_in(ao, chunk);
|
||||||
g_mutex_unlock(ao->mutex);
|
g_mutex_unlock(ao->mutex);
|
||||||
@ -339,14 +349,15 @@ clear_tail_chunk(G_GNUC_UNUSED const struct music_chunk *chunk, bool *locked)
|
|||||||
for (unsigned i = 0; i < num_audio_outputs; ++i) {
|
for (unsigned i = 0; i < num_audio_outputs; ++i) {
|
||||||
struct audio_output *ao = &audio_outputs[i];
|
struct audio_output *ao = &audio_outputs[i];
|
||||||
|
|
||||||
locked[i] = ao->open;
|
|
||||||
|
|
||||||
if (!locked[i])
|
|
||||||
continue;
|
|
||||||
|
|
||||||
/* this mutex will be unlocked by the caller when it's
|
/* this mutex will be unlocked by the caller when it's
|
||||||
ready */
|
ready */
|
||||||
g_mutex_lock(ao->mutex);
|
g_mutex_lock(ao->mutex);
|
||||||
|
locked[i] = ao->open;
|
||||||
|
|
||||||
|
if (!locked[i]) {
|
||||||
|
g_mutex_unlock(ao->mutex);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
assert(ao->chunk == chunk);
|
assert(ao->chunk == chunk);
|
||||||
assert(ao->chunk_finished);
|
assert(ao->chunk_finished);
|
||||||
|
@ -65,6 +65,11 @@ struct audio_output {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Is the device (already) open and functional?
|
* Is the device (already) open and functional?
|
||||||
|
*
|
||||||
|
* This attribute may only be modified by the output thread.
|
||||||
|
* It is protected with #mutex: write accesses inside the
|
||||||
|
* output thread and read accesses outside of it may only be
|
||||||
|
* performed while the lock is held.
|
||||||
*/
|
*/
|
||||||
bool open;
|
bool open;
|
||||||
|
|
||||||
@ -113,7 +118,7 @@ struct audio_output {
|
|||||||
const struct music_pipe *pipe;
|
const struct music_pipe *pipe;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This mutex protects #chunk and #chunk_finished.
|
* This mutex protects #open, #chunk and #chunk_finished.
|
||||||
*/
|
*/
|
||||||
GMutex *mutex;
|
GMutex *mutex;
|
||||||
|
|
||||||
|
@ -48,11 +48,11 @@ ao_close(struct audio_output *ao)
|
|||||||
|
|
||||||
g_mutex_lock(ao->mutex);
|
g_mutex_lock(ao->mutex);
|
||||||
ao->chunk = NULL;
|
ao->chunk = NULL;
|
||||||
|
ao->open = false;
|
||||||
g_mutex_unlock(ao->mutex);
|
g_mutex_unlock(ao->mutex);
|
||||||
|
|
||||||
ao_plugin_close(ao->plugin, ao->data);
|
ao_plugin_close(ao->plugin, ao->data);
|
||||||
pcm_convert_deinit(&ao->convert_state);
|
pcm_convert_deinit(&ao->convert_state);
|
||||||
ao->open = false;
|
|
||||||
|
|
||||||
g_debug("closed plugin=%s name=\"%s\"", ao->plugin->name, ao->name);
|
g_debug("closed plugin=%s name=\"%s\"", ao->plugin->name, ao->name);
|
||||||
}
|
}
|
||||||
@ -198,8 +198,10 @@ static gpointer audio_output_task(gpointer arg)
|
|||||||
assert(!ao->open);
|
assert(!ao->open);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
pcm_convert_init(&ao->convert_state);
|
pcm_convert_init(&ao->convert_state);
|
||||||
ao->open = true;
|
|
||||||
|
|
||||||
|
g_mutex_lock(ao->mutex);
|
||||||
|
ao->open = true;
|
||||||
|
g_mutex_unlock(ao->mutex);
|
||||||
|
|
||||||
g_debug("opened plugin=%s name=\"%s\" "
|
g_debug("opened plugin=%s name=\"%s\" "
|
||||||
"audio_format=%u:%u:%u",
|
"audio_format=%u:%u:%u",
|
||||||
|
Loading…
Reference in New Issue
Block a user