Commit 7d3921bb authored by Max Kellermann's avatar Max Kellermann

pulse_mixer: protect the struct with a mutex

There are numerous race conditions between the libpulse thread (pulse_mixer.c callbacks) and the rest of MPD. Protect the volatile attributes of the pulse_mixer struct with a mutex to fix that.
parent 7f762a5c
...@@ -36,6 +36,8 @@ struct pulse_mixer { ...@@ -36,6 +36,8 @@ struct pulse_mixer {
const char *sink; const char *sink;
const char *output_name; const char *output_name;
GMutex *mutex;
uint32_t index; uint32_t index;
bool online; bool online;
...@@ -95,9 +97,13 @@ sink_input_cb(G_GNUC_UNUSED pa_context *context, const pa_sink_input_info *i, ...@@ -95,9 +97,13 @@ sink_input_cb(G_GNUC_UNUSED pa_context *context, const pa_sink_input_info *i,
g_debug("sink input cb %s, index %d ",i->name,i->index); g_debug("sink input cb %s, index %d ",i->name,i->index);
if (strcmp(i->name,pm->output_name) == 0) { if (strcmp(i->name,pm->output_name) == 0) {
g_mutex_lock(pm->mutex);
pm->index = i->index; pm->index = i->index;
pm->online = true; pm->online = true;
pm->volume = i->volume; pm->volume = i->volume;
g_mutex_unlock(pm->mutex);
} else } else
g_debug("bad name"); g_debug("bad name");
} }
...@@ -120,7 +126,10 @@ sink_input_vol(G_GNUC_UNUSED pa_context *context, const pa_sink_input_info *i, ...@@ -120,7 +126,10 @@ sink_input_vol(G_GNUC_UNUSED pa_context *context, const pa_sink_input_info *i,
} }
g_debug("sink input vol %s, index %d ", i->name, i->index); g_debug("sink input vol %s, index %d ", i->name, i->index);
g_mutex_lock(pm->mutex);
pm->volume = i->volume; pm->volume = i->volume;
g_mutex_unlock(pm->mutex);
pa_threaded_mainloop_signal(pm->mainloop, 0); pa_threaded_mainloop_signal(pm->mainloop, 0);
} }
...@@ -136,6 +145,8 @@ subscribe_cb(pa_context *c, pa_subscription_event_type_t t, ...@@ -136,6 +145,8 @@ subscribe_cb(pa_context *c, pa_subscription_event_type_t t,
switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) { switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) {
case PA_SUBSCRIPTION_EVENT_SINK_INPUT: case PA_SUBSCRIPTION_EVENT_SINK_INPUT:
g_mutex_lock(pm->mutex);
if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) ==
PA_SUBSCRIPTION_EVENT_REMOVE && PA_SUBSCRIPTION_EVENT_REMOVE &&
pm->index == idx) pm->index == idx)
...@@ -146,12 +157,15 @@ subscribe_cb(pa_context *c, pa_subscription_event_type_t t, ...@@ -146,12 +157,15 @@ subscribe_cb(pa_context *c, pa_subscription_event_type_t t,
o = pa_context_get_sink_input_info(c, idx, o = pa_context_get_sink_input_info(c, idx,
sink_input_cb, pm); sink_input_cb, pm);
if (o == NULL) { if (o == NULL) {
g_mutex_unlock(pm->mutex);
g_debug("pa_context_get_sink_input_info() failed"); g_debug("pa_context_get_sink_input_info() failed");
return; return;
} }
pa_operation_unref(o); pa_operation_unref(o);
} }
g_mutex_unlock(pm->mutex);
break; break;
} }
} }
...@@ -215,6 +229,8 @@ pulse_mixer_init(const struct config_param *param) ...@@ -215,6 +229,8 @@ pulse_mixer_init(const struct config_param *param)
pm->sink = config_get_block_string(param, "sink", NULL); pm->sink = config_get_block_string(param, "sink", NULL);
pm->output_name = config_get_block_string(param, "name", NULL); pm->output_name = config_get_block_string(param, "name", NULL);
pm->mutex = g_mutex_new();
return &pm->base; return &pm->base;
} }
...@@ -223,6 +239,7 @@ pulse_mixer_finish(struct mixer *data) ...@@ -223,6 +239,7 @@ pulse_mixer_finish(struct mixer *data)
{ {
struct pulse_mixer *pm = (struct pulse_mixer *) data; struct pulse_mixer *pm = (struct pulse_mixer *) data;
g_mutex_free(pm->mutex);
g_free(pm); g_free(pm);
} }
...@@ -314,12 +331,15 @@ pulse_mixer_get_volume(struct mixer *mixer) ...@@ -314,12 +331,15 @@ pulse_mixer_get_volume(struct mixer *mixer)
int ret; int ret;
pa_operation *o; pa_operation *o;
g_mutex_lock(pm->mutex);
if (!pm->online) { if (!pm->online) {
g_mutex_unlock(pm->mutex);
return false; return false;
} }
o = pa_context_get_sink_input_info(pm->context, pm->index, o = pa_context_get_sink_input_info(pm->context, pm->index,
sink_input_vol, pm); sink_input_vol, pm);
g_mutex_unlock(pm->mutex);
if (o == NULL) { if (o == NULL) {
g_debug("pa_context_get_sink_input_info() failed"); g_debug("pa_context_get_sink_input_info() failed");
return false; return false;
...@@ -328,7 +348,12 @@ pulse_mixer_get_volume(struct mixer *mixer) ...@@ -328,7 +348,12 @@ pulse_mixer_get_volume(struct mixer *mixer)
if (!pulse_wait_for_operation(pm->mainloop, o)) if (!pulse_wait_for_operation(pm->mainloop, o))
return false; return false;
ret = (int)((100*(pa_cvolume_avg(&pm->volume)+1))/PA_VOLUME_NORM); g_mutex_lock(pm->mutex);
ret = pm->online
? (int)((100*(pa_cvolume_avg(&pm->volume)+1))/PA_VOLUME_NORM)
: -1;
g_mutex_unlock(pm->mutex);
return ret; return ret;
} }
...@@ -339,7 +364,9 @@ pulse_mixer_set_volume(struct mixer *mixer, unsigned volume) ...@@ -339,7 +364,9 @@ pulse_mixer_set_volume(struct mixer *mixer, unsigned volume)
struct pa_cvolume cvolume; struct pa_cvolume cvolume;
pa_operation *o; pa_operation *o;
g_mutex_lock(pm->mutex);
if (!pm->online) { if (!pm->online) {
g_mutex_unlock(pm->mutex);
return false; return false;
} }
...@@ -348,6 +375,7 @@ pulse_mixer_set_volume(struct mixer *mixer, unsigned volume) ...@@ -348,6 +375,7 @@ pulse_mixer_set_volume(struct mixer *mixer, unsigned volume)
o = pa_context_set_sink_input_volume(pm->context, pm->index, o = pa_context_set_sink_input_volume(pm->context, pm->index,
&cvolume, NULL, NULL); &cvolume, NULL, NULL);
g_mutex_unlock(pm->mutex);
if (o == NULL) { if (o == NULL) {
g_debug("pa_context_set_sink_input_volume() failed"); g_debug("pa_context_set_sink_input_volume() failed");
return false; return false;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment