[pulseaudio-discuss] [PATCH 3/5] alsa-sink: Add locking to protect concurrent mixer and mixer_path access
Jyri Sarha
oku at iki.fi
Sun Feb 28 05:13:07 PST 2010
From: Jyri Sarha <jyri.sarha at nokia.com>
---
src/modules/alsa/alsa-sink.c | 55 ++++++++++++++++++++++++++++++++++++++---
1 files changed, 51 insertions(+), 4 deletions(-)
diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c
index 45ba24a..43b0b09 100644
--- a/src/modules/alsa/alsa-sink.c
+++ b/src/modules/alsa/alsa-sink.c
@@ -98,6 +98,12 @@ struct userdata {
snd_mixer_t *mixer_handle;
pa_alsa_path_set *mixer_path_set;
pa_alsa_path *mixer_path;
+ /* NOTE: This lock is a crude way of protecting both mixer_path and mixer_element
+ * when accessing them from main and IO-thread concurrently. Everything
+ * seem to work fine even without this lock when stress testing the sync
+ * volume with multiple concurrent transient streams, but better safe than
+ * sorry. */
+ pa_mutex *mixer_mutex;
pa_cvolume hardware_volume;
@@ -1133,12 +1139,16 @@ static void sink_get_volume_cb(pa_sink *s) {
struct userdata *u = s->userdata;
pa_cvolume r;
char t[PA_CVOLUME_SNPRINT_MAX];
+ int status;
pa_assert(u);
pa_assert(u->mixer_path);
pa_assert(u->mixer_handle);
- if (pa_alsa_path_get_volume(u->mixer_path, u->mixer_handle, &s->channel_map, &r) < 0)
+ pa_mutex_lock(u->mixer_mutex);
+ status = pa_alsa_path_get_volume(u->mixer_path, u->mixer_handle, &s->channel_map, &r);
+ pa_mutex_unlock(u->mixer_mutex);
+ if (status < 0)
return;
/* Shift down by the base volume, so that 0dB becomes maximum volume */
@@ -1161,6 +1171,7 @@ static void sink_set_volume_cb(pa_sink *s) {
pa_cvolume r;
char t[PA_CVOLUME_SNPRINT_MAX];
pa_bool_t write_to_hw = (s->flags & PA_SINK_SYNC_VOLUME) ? FALSE : TRUE;
+ int status;
pa_assert(u);
pa_assert(u->mixer_path);
@@ -1169,7 +1180,10 @@ static void sink_set_volume_cb(pa_sink *s) {
/* Shift up by the base volume */
pa_sw_cvolume_divide_scalar(&r, &s->real_volume, s->base_volume);
- if (pa_alsa_path_set_volume(u->mixer_path, u->mixer_handle, &s->channel_map, &r, write_to_hw) < 0)
+ pa_mutex_lock(u->mixer_mutex);
+ status = pa_alsa_path_set_volume(u->mixer_path, u->mixer_handle, &s->channel_map, &r, write_to_hw);
+ pa_mutex_unlock(u->mixer_mutex);
+ if (status < 0)
return;
/* Shift down by the base volume, so that 0dB becomes maximum volume */
@@ -1212,13 +1226,18 @@ static void sink_set_volume_cb(pa_sink *s) {
static void sink_write_volume_cb(pa_sink *s) {
struct userdata *u = s->userdata;
pa_cvolume hw_vol = s->thread_info.current_hw_volume;
+ int status;
pa_assert(u);
pa_assert(u->mixer_path);
pa_assert(u->mixer_handle);
pa_assert(s->flags & PA_SINK_SYNC_VOLUME);
- if (pa_alsa_path_set_volume(u->mixer_path, u->mixer_handle, &s->channel_map, &hw_vol, TRUE) < 0)
+ pa_mutex_lock(u->mixer_mutex);
+ status = pa_alsa_path_set_volume(u->mixer_path, u->mixer_handle, &s->channel_map, &hw_vol, TRUE);
+ pa_mutex_unlock(u->mixer_mutex);
+
+ if (status < 0)
pa_log_error("Writting HW volume failed");
else {
pa_cvolume tmp_vol;
@@ -1239,12 +1258,16 @@ static void sink_write_volume_cb(pa_sink *s) {
static void sink_get_mute_cb(pa_sink *s) {
struct userdata *u = s->userdata;
pa_bool_t b;
+ int status;
pa_assert(u);
pa_assert(u->mixer_path);
pa_assert(u->mixer_handle);
- if (pa_alsa_path_get_mute(u->mixer_path, u->mixer_handle, &b) < 0)
+ pa_mutex_lock(u->mixer_mutex);
+ status = pa_alsa_path_get_mute(u->mixer_path, u->mixer_handle, &b);
+ pa_mutex_unlock(u->mixer_mutex);
+ if (status < 0)
return;
s->muted = b;
@@ -1257,7 +1280,9 @@ static void sink_set_mute_cb(pa_sink *s) {
pa_assert(u->mixer_path);
pa_assert(u->mixer_handle);
+ pa_mutex_lock(u->mixer_mutex);
pa_alsa_path_set_mute(u->mixer_path, u->mixer_handle, s->muted);
+ pa_mutex_unlock(u->mixer_mutex);
}
static int sink_set_port_cb(pa_sink *s, pa_device_port *p) {
@@ -1271,6 +1296,13 @@ static int sink_set_port_cb(pa_sink *s, pa_device_port *p) {
data = PA_DEVICE_PORT_DATA(p);
pa_assert_se(u->mixer_path = data->path);
+
+ /* This forces flushing of pending volume change events */
+ if (u->sink->flags & PA_SINK_SYNC_VOLUME)
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_VOLUME, NULL, 0, NULL) == 0);
+
+ pa_mutex_lock(u->mixer_mutex);
+
pa_alsa_path_select(u->mixer_path, u->mixer_handle);
if (u->mixer_path->has_volume && u->mixer_path->has_dB) {
@@ -1289,10 +1321,16 @@ static int sink_set_port_cb(pa_sink *s, pa_device_port *p) {
if (data->setting)
pa_alsa_setting_select(data->setting, u->mixer_handle);
+ pa_mutex_unlock(u->mixer_mutex);
+
if (s->set_mute)
s->set_mute(s);
if (s->set_volume)
s->set_volume(s);
+ /* NOTE: This is a bit unorthodox to call this from main thread,
+ * but everything is mutex protected anyway. */
+ if (s->write_volume)
+ s->write_volume(s);
return 0;
}
@@ -1796,6 +1834,11 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
TRUE);
u->smoother_interval = SMOOTHER_MIN_INTERVAL;
+ if (!(u->mixer_mutex = pa_mutex_new(TRUE, FALSE))) {
+ pa_log_error("Could not allocate pa_mutex");
+ goto fail;
+ }
+
dev_id = pa_modargs_get_value(
ma, "device_id",
pa_modargs_get_value(ma, "device", DEFAULT_DEVICE));
@@ -2064,6 +2107,10 @@ static void userdata_free(struct userdata *u) {
snd_pcm_close(u->pcm_handle);
}
+ if (u->mixer_mutex) {
+ pa_mutex_free(u->mixer_mutex);
+ }
+
if (u->mixer_fdl)
pa_alsa_fdlist_free(u->mixer_fdl);
--
1.7.0
More information about the pulseaudio-discuss
mailing list