[pulseaudio-commits] Branch 'next' - 2 commits - src/modules src/pulsecore

Tanu Kaskinen tanuk at kemper.freedesktop.org
Tue Mar 20 11:00:34 UTC 2018


 src/modules/alsa/alsa-sink.c                 |   99 ++++++++++++++++++++----
 src/modules/alsa/alsa-source.c               |   99 ++++++++++++++++++++----
 src/modules/bluetooth/module-bluez4-device.c |    4 -
 src/modules/bluetooth/module-bluez5-device.c |    4 -
 src/modules/echo-cancel/module-echo-cancel.c |    2 
 src/modules/module-combine-sink.c            |    7 +
 src/modules/module-equalizer-sink.c          |    2 
 src/modules/module-esound-sink.c             |    7 +
 src/modules/module-ladspa-sink.c             |    2 
 src/modules/module-null-sink.c               |    2 
 src/modules/module-null-source.c             |    2 
 src/modules/module-pipe-sink.c               |    2 
 src/modules/module-remap-sink.c              |    2 
 src/modules/module-sine-source.c             |    2 
 src/modules/module-solaris.c                 |   14 +++
 src/modules/module-tunnel-sink-new.c         |    7 +
 src/modules/module-tunnel-source-new.c       |    7 +
 src/modules/module-virtual-sink.c            |    2 
 src/modules/module-virtual-surround-sink.c   |    2 
 src/modules/oss/module-oss.c                 |   14 +++
 src/modules/raop/raop-sink.c                 |    7 +
 src/pulsecore/sink.c                         |  107 +++++++++++----------------
 src/pulsecore/sink.h                         |    9 --
 src/pulsecore/source.c                       |  107 +++++++++++----------------
 src/pulsecore/source.h                       |    9 --
 25 files changed, 322 insertions(+), 199 deletions(-)

New commits:
commit 3b0d5f8d161dd3332b8611dd4e7ba2fa5f8019ec
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Mon Mar 19 23:11:05 2018 +0200

    fix a call to pa_sink_suspend() from an incorrect thread
    
    The alsa sink calls pa_sink_suspend() from the set_port() callback.
    pa_sink_suspend() can only be called from the main thread, but the
    set_port() callback was often called from the IO thread. That caused an
    assertion to be hit in pa_sink_suspend() when switching ports.
    
    Another issue was that pa_sink_suspend() called the set_port() callback,
    and if the callback calls pa_sink_suspend() again recursively, nothing
    good can be expected from that, so the thread mismatch was not the only
    problem.
    
    This patch moves the mixer syncing logic out of pa_sink/source_suspend()
    to be handled internally by the alsa sink/source. This removes the
    recursive pa_sink_suspend() call. This also removes the need to have the
    mixer_dirty flag in pa_sink/source, so the flag and the
    pa_sink/source_set_mixer_dirty() functions can be removed.
    
    This patch also changes the threading rules of set_port(). Previously it
    was called sometimes from the main thread and sometimes from the IO
    thread. Now it's always called from the main thread. When deferred
    volumes are used, the alsa sink and source still have to update the
    mixer from the IO thread when switching ports, but the thread
    synchronization is now handled internally by the alsa sink and source.
    The SET_PORT messages are not needed any more and can be removed.
    
    BugLink: https://bugs.freedesktop.org/show_bug.cgi?id=104761

diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c
index 6551a0b1..a1f10057 100644
--- a/src/modules/alsa/alsa-sink.c
+++ b/src/modules/alsa/alsa-sink.c
@@ -161,6 +161,10 @@ struct userdata {
     pa_alsa_ucm_mapping_context *ucm_context;
 };
 
+enum {
+    SINK_MESSAGE_SYNC_MIXER = PA_SINK_MESSAGE_MAX
+};
+
 static void userdata_free(struct userdata *u);
 
 /* FIXME: Is there a better way to do this than device names? */
@@ -1168,6 +1172,43 @@ fail:
     return -PA_ERR_IO;
 }
 
+/* Called from the IO thread or the main thread depending on whether deferred
+ * volume is enabled or not (with deferred volume all mixer handling is done
+ * from the IO thread).
+ *
+ * Sets the mixer settings to match the current sink and port state (the port
+ * is given as an argument, because active_port may still point to the old
+ * port, if we're switching ports). */
+static void sync_mixer(struct userdata *u, pa_device_port *port) {
+    pa_alsa_setting *setting = NULL;
+
+    pa_assert(u);
+
+    if (!u->mixer_path)
+        return;
+
+    /* port may be NULL, because if we use a synthesized mixer path, then the
+     * sink has no ports. */
+    if (port) {
+        pa_alsa_port_data *data;
+
+        data = PA_DEVICE_PORT_DATA(port);
+        setting = data->setting;
+    }
+
+    pa_alsa_path_select(u->mixer_path, setting, u->mixer_handle, u->sink->muted);
+
+    if (u->sink->set_mute)
+        u->sink->set_mute(u->sink);
+    if (u->sink->flags & PA_SINK_DEFERRED_VOLUME) {
+        if (u->sink->write_volume)
+            u->sink->write_volume(u->sink);
+    } else {
+        if (u->sink->set_volume)
+            u->sink->set_volume(u->sink);
+    }
+}
+
 /* Called from IO context */
 static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
     struct userdata *u = PA_SINK(o)->userdata;
@@ -1184,6 +1225,13 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
 
             return 0;
         }
+
+        case SINK_MESSAGE_SYNC_MIXER: {
+            pa_device_port *port = data;
+
+            sync_mixer(u, port);
+            return 0;
+        }
     }
 
     return pa_sink_process_msg(o, code, data, offset, chunk);
@@ -1197,6 +1245,16 @@ static int sink_set_state_in_main_thread_cb(pa_sink *s, pa_sink_state_t new_stat
     pa_sink_assert_ref(s);
     pa_assert_se(u = s->userdata);
 
+    /* When our session becomes active, we need to sync the mixer, because
+     * another user may have changed the mixer settings.
+     *
+     * If deferred volume is enabled, the syncing is done in the
+     * set_state_in_io_thread() callback instead. */
+    if (!(s->flags & PA_SINK_DEFERRED_VOLUME)
+            && (s->suspend_cause & PA_SUSPEND_SESSION)
+            && !(new_suspend_cause & PA_SUSPEND_SESSION))
+        sync_mixer(u, s->active_port);
+
     old_state = pa_sink_get_state(u->sink);
 
     if (PA_SINK_IS_OPENED(old_state) && new_state == PA_SINK_SUSPENDED)
@@ -1215,8 +1273,18 @@ static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state,
     pa_assert(s);
     pa_assert_se(u = s->userdata);
 
+    /* When our session becomes active, we need to sync the mixer, because
+     * another user may have changed the mixer settings.
+     *
+     * If deferred volume is disabled, the syncing is done in the
+     * set_state_in_main_thread() callback instead. */
+    if ((s->flags & PA_SINK_DEFERRED_VOLUME)
+            && (s->suspend_cause & PA_SUSPEND_SESSION)
+            && !(new_suspend_cause & PA_SUSPEND_SESSION))
+        sync_mixer(u, s->active_port);
+
     /* It may be that only the suspend cause is changing, in which case there's
-     * nothing to do. */
+     * nothing more to do. */
     if (new_state == s->thread_info.state)
         return 0;
 
@@ -1273,10 +1341,8 @@ static int ctl_mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) {
     if (!PA_SINK_IS_LINKED(u->sink->state))
         return 0;
 
-    if (u->sink->suspend_cause & PA_SUSPEND_SESSION) {
-        pa_sink_set_mixer_dirty(u->sink, true);
+    if (u->sink->suspend_cause & PA_SUSPEND_SESSION)
         return 0;
-    }
 
     if (mask & SND_CTL_EVENT_MASK_VALUE) {
         pa_sink_get_volume(u->sink, true);
@@ -1295,10 +1361,8 @@ static int io_mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) {
     if (mask == SND_CTL_EVENT_MASK_REMOVE)
         return 0;
 
-    if (u->sink->suspend_cause & PA_SUSPEND_SESSION) {
-        pa_sink_set_mixer_dirty(u->sink, true);
+    if (u->sink->suspend_cause & PA_SUSPEND_SESSION)
         return 0;
-    }
 
     if (mask & SND_CTL_EVENT_MASK_VALUE)
         pa_sink_update_volume_and_mute(u->sink);
@@ -1522,21 +1586,13 @@ static int sink_set_port_cb(pa_sink *s, pa_device_port *p) {
     pa_assert(u->mixer_handle);
 
     data = PA_DEVICE_PORT_DATA(p);
-
     pa_assert_se(u->mixer_path = data->path);
-    pa_alsa_path_select(u->mixer_path, data->setting, u->mixer_handle, s->muted);
-
     mixer_volume_init(u);
 
-    if (s->set_mute)
-        s->set_mute(s);
-    if (s->flags & PA_SINK_DEFERRED_VOLUME) {
-        if (s->write_volume)
-            s->write_volume(s);
-    } else {
-        if (s->set_volume)
-            s->set_volume(s);
-    }
+    if (s->flags & PA_SINK_DEFERRED_VOLUME)
+        pa_asyncmsgq_send(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_SYNC_MIXER, p, 0, NULL);
+    else
+        sync_mixer(u, p);
 
     if (data->suspend_when_unavailable && p->available == PA_AVAILABLE_NO)
         pa_sink_suspend(s, true, PA_SUSPEND_UNAVAILABLE);
diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c
index 94ec999b..d2e0a43f 100644
--- a/src/modules/alsa/alsa-source.c
+++ b/src/modules/alsa/alsa-source.c
@@ -143,6 +143,10 @@ struct userdata {
     pa_alsa_ucm_mapping_context *ucm_context;
 };
 
+enum {
+    SOURCE_MESSAGE_SYNC_MIXER = PA_SOURCE_MESSAGE_MAX
+};
+
 static void userdata_free(struct userdata *u);
 
 static pa_hook_result_t reserve_cb(pa_reserve_wrapper *r, void *forced, struct userdata *u) {
@@ -1023,6 +1027,43 @@ fail:
     return -PA_ERR_IO;
 }
 
+/* Called from the IO thread or the main thread depending on whether deferred
+ * volume is enabled or not (with deferred volume all mixer handling is done
+ * from the IO thread).
+ *
+ * Sets the mixer settings to match the current source and port state (the port
+ * is given as an argument, because active_port may still point to the old
+ * port, if we're switching ports). */
+static void sync_mixer(struct userdata *u, pa_device_port *port) {
+    pa_alsa_setting *setting = NULL;
+
+    pa_assert(u);
+
+    if (!u->mixer_path)
+        return;
+
+    /* port may be NULL, because if we use a synthesized mixer path, then the
+     * source has no ports. */
+    if (port) {
+        pa_alsa_port_data *data;
+
+        data = PA_DEVICE_PORT_DATA(port);
+        setting = data->setting;
+    }
+
+    pa_alsa_path_select(u->mixer_path, setting, u->mixer_handle, u->source->muted);
+
+    if (u->source->set_mute)
+        u->source->set_mute(u->source);
+    if (u->source->flags & PA_SOURCE_DEFERRED_VOLUME) {
+        if (u->source->write_volume)
+            u->source->write_volume(u->source);
+    } else {
+        if (u->source->set_volume)
+            u->source->set_volume(u->source);
+    }
+}
+
 /* Called from IO context */
 static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
     struct userdata *u = PA_SOURCE(o)->userdata;
@@ -1039,6 +1080,13 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
 
             return 0;
         }
+
+        case SOURCE_MESSAGE_SYNC_MIXER: {
+            pa_device_port *port = data;
+
+            sync_mixer(u, port);
+            return 0;
+        }
     }
 
     return pa_source_process_msg(o, code, data, offset, chunk);
@@ -1052,6 +1100,16 @@ static int source_set_state_in_main_thread_cb(pa_source *s, pa_source_state_t ne
     pa_source_assert_ref(s);
     pa_assert_se(u = s->userdata);
 
+    /* When our session becomes active, we need to sync the mixer, because
+     * another user may have changed the mixer settings.
+     *
+     * If deferred volume is enabled, the syncing is done in the
+     * set_state_in_io_thread() callback instead. */
+    if (!(s->flags & PA_SOURCE_DEFERRED_VOLUME)
+            && (s->suspend_cause & PA_SUSPEND_SESSION)
+            && !(new_suspend_cause & PA_SUSPEND_SESSION))
+        sync_mixer(u, s->active_port);
+
     old_state = pa_source_get_state(u->source);
 
     if (PA_SOURCE_IS_OPENED(old_state) && new_state == PA_SOURCE_SUSPENDED)
@@ -1070,8 +1128,18 @@ static int source_set_state_in_io_thread_cb(pa_source *s, pa_source_state_t new_
     pa_assert(s);
     pa_assert_se(u = s->userdata);
 
+    /* When our session becomes active, we need to sync the mixer, because
+     * another user may have changed the mixer settings.
+     *
+     * If deferred volume is disabled, the syncing is done in the
+     * set_state_in_main_thread() callback instead. */
+    if ((s->flags & PA_SOURCE_DEFERRED_VOLUME)
+            && (s->suspend_cause & PA_SUSPEND_SESSION)
+            && !(new_suspend_cause & PA_SUSPEND_SESSION))
+        sync_mixer(u, s->active_port);
+
     /* It may be that only the suspend cause is changing, in which case there's
-     * nothing to do. */
+     * nothing more to do. */
     if (new_state == s->thread_info.state)
         return 0;
 
@@ -1128,10 +1196,8 @@ static int ctl_mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) {
     if (!PA_SOURCE_IS_LINKED(u->source->state))
         return 0;
 
-    if (u->source->suspend_cause & PA_SUSPEND_SESSION) {
-        pa_source_set_mixer_dirty(u->source, true);
+    if (u->source->suspend_cause & PA_SUSPEND_SESSION)
         return 0;
-    }
 
     if (mask & SND_CTL_EVENT_MASK_VALUE) {
         pa_source_get_volume(u->source, true);
@@ -1150,10 +1216,8 @@ static int io_mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) {
     if (mask == SND_CTL_EVENT_MASK_REMOVE)
         return 0;
 
-    if (u->source->suspend_cause & PA_SUSPEND_SESSION) {
-        pa_source_set_mixer_dirty(u->source, true);
+    if (u->source->suspend_cause & PA_SUSPEND_SESSION)
         return 0;
-    }
 
     if (mask & SND_CTL_EVENT_MASK_VALUE)
         pa_source_update_volume_and_mute(u->source);
@@ -1377,21 +1441,13 @@ static int source_set_port_cb(pa_source *s, pa_device_port *p) {
     pa_assert(u->mixer_handle);
 
     data = PA_DEVICE_PORT_DATA(p);
-
     pa_assert_se(u->mixer_path = data->path);
-    pa_alsa_path_select(u->mixer_path, data->setting, u->mixer_handle, s->muted);
-
     mixer_volume_init(u);
 
-    if (s->set_mute)
-        s->set_mute(s);
-    if (s->flags & PA_SOURCE_DEFERRED_VOLUME) {
-        if (s->write_volume)
-            s->write_volume(s);
-    } else {
-        if (s->set_volume)
-            s->set_volume(s);
-    }
+    if (s->flags & PA_SOURCE_DEFERRED_VOLUME)
+        pa_asyncmsgq_send(u->source->asyncmsgq, PA_MSGOBJECT(u->source), SOURCE_MESSAGE_SYNC_MIXER, p, 0, NULL);
+    else
+        sync_mixer(u, p);
 
     return 0;
 }
diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index 2174354f..b801b6bc 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -64,11 +64,6 @@ struct pa_sink_volume_change {
     PA_LLIST_FIELDS(pa_sink_volume_change);
 };
 
-struct sink_message_set_port {
-    pa_device_port *port;
-    int ret;
-};
-
 struct set_state_data {
     pa_sink_state_t state;
     pa_suspend_cause_t suspend_cause;
@@ -259,7 +254,6 @@ pa_sink* pa_sink_new(
     s->flags = flags;
     s->priority = 0;
     s->suspend_cause = data->suspend_cause;
-    pa_sink_set_mixer_dirty(s, false);
     s->name = pa_xstrdup(name);
     s->proplist = pa_proplist_copy(data->proplist);
     s->driver = pa_xstrdup(pa_path_get_filename(data->driver));
@@ -897,11 +891,6 @@ int pa_sink_update_status(pa_sink*s) {
     return sink_set_state(s, pa_sink_used_by(s) ? PA_SINK_RUNNING : PA_SINK_IDLE, 0);
 }
 
-/* Called from any context - must be threadsafe */
-void pa_sink_set_mixer_dirty(pa_sink *s, bool is_dirty) {
-    pa_atomic_store(&s->mixer_dirty, is_dirty ? 1 : 0);
-}
-
 /* Called from main context */
 int pa_sink_suspend(pa_sink *s, bool suspend, pa_suspend_cause_t cause) {
     pa_suspend_cause_t merged_cause;
@@ -916,27 +905,6 @@ int pa_sink_suspend(pa_sink *s, bool suspend, pa_suspend_cause_t cause) {
     else
         merged_cause = s->suspend_cause & ~cause;
 
-    if (!(merged_cause & PA_SUSPEND_SESSION) && (pa_atomic_load(&s->mixer_dirty) != 0)) {
-        /* This might look racy but isn't: If somebody sets mixer_dirty exactly here,
-           it'll be handled just fine. */
-        pa_sink_set_mixer_dirty(s, false);
-        pa_log_debug("Mixer is now accessible. Updating alsa mixer settings.");
-        if (s->active_port && s->set_port) {
-            if (s->flags & PA_SINK_DEFERRED_VOLUME) {
-                struct sink_message_set_port msg = { .port = s->active_port, .ret = 0 };
-                pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_PORT, &msg, 0, NULL) == 0);
-            }
-            else
-                s->set_port(s, s->active_port);
-        }
-        else {
-            if (s->set_mute)
-                s->set_mute(s);
-            if (s->set_volume)
-                s->set_volume(s);
-        }
-    }
-
     if (merged_cause)
         return sink_set_state(s, PA_SINK_SUSPENDED, merged_cause);
     else
@@ -2969,15 +2937,6 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
             pa_sink_set_max_request_within_thread(s, (size_t) offset);
             return 0;
 
-        case PA_SINK_MESSAGE_SET_PORT:
-
-            pa_assert(userdata);
-            if (s->set_port) {
-                struct sink_message_set_port *msg_data = userdata;
-                msg_data->ret = s->set_port(s, msg_data->port);
-            }
-            return 0;
-
         case PA_SINK_MESSAGE_UPDATE_VOLUME_AND_MUTE:
             /* This message is sent from IO-thread and handled in main thread. */
             pa_assert_ctl_context();
@@ -3432,7 +3391,6 @@ size_t pa_sink_get_max_request(pa_sink *s) {
 /* Called from main context */
 int pa_sink_set_port(pa_sink *s, const char *name, bool save) {
     pa_device_port *port;
-    int ret;
 
     pa_sink_assert_ref(s);
     pa_assert_ctl_context();
@@ -3453,15 +3411,7 @@ int pa_sink_set_port(pa_sink *s, const char *name, bool save) {
         return 0;
     }
 
-    if (s->flags & PA_SINK_DEFERRED_VOLUME) {
-        struct sink_message_set_port msg = { .port = port, .ret = 0 };
-        pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_PORT, &msg, 0, NULL) == 0);
-        ret = msg.ret;
-    }
-    else
-        ret = s->set_port(s, port);
-
-    if (ret < 0)
+    if (s->set_port(s, port) < 0)
         return -PA_ERR_NOENTITY;
 
     pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h
index 3e9191d2..abf2d924 100644
--- a/src/pulsecore/sink.h
+++ b/src/pulsecore/sink.h
@@ -115,7 +115,6 @@ struct pa_sink {
 
     pa_hashmap *ports;
     pa_device_port *active_port;
-    pa_atomic_t mixer_dirty;
 
     /* The latency offset is inherited from the currently active port */
     int64_t port_latency_offset;
@@ -251,8 +250,8 @@ struct pa_sink {
      * thread context. */
     pa_sink_cb_t update_requested_latency; /* may be NULL */
 
-    /* Called whenever the port shall be changed. Called from IO
-     * thread if deferred volumes are enabled, and main thread otherwise. */
+    /* Called whenever the port shall be changed. Called from the main
+     * thread. */
     int (*set_port)(pa_sink *s, pa_device_port *port); /* may be NULL */
 
     /* Called to get the list of formats supported by the sink, sorted
@@ -356,7 +355,6 @@ typedef enum pa_sink_message {
     PA_SINK_MESSAGE_GET_MAX_REQUEST,
     PA_SINK_MESSAGE_SET_MAX_REWIND,
     PA_SINK_MESSAGE_SET_MAX_REQUEST,
-    PA_SINK_MESSAGE_SET_PORT,
     PA_SINK_MESSAGE_UPDATE_VOLUME_AND_MUTE,
     PA_SINK_MESSAGE_SET_PORT_LATENCY_OFFSET,
     PA_SINK_MESSAGE_MAX
@@ -483,7 +481,6 @@ bool pa_sink_get_mute(pa_sink *sink, bool force_refresh);
 bool pa_sink_update_proplist(pa_sink *s, pa_update_mode_t mode, pa_proplist *p);
 
 int pa_sink_set_port(pa_sink *s, const char *name, bool save);
-void pa_sink_set_mixer_dirty(pa_sink *s, bool is_dirty);
 
 unsigned pa_sink_linked_by(pa_sink *s); /* Number of connected streams */
 unsigned pa_sink_used_by(pa_sink *s); /* Number of connected streams which are not corked */
diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
index e04463c6..ffb8b6ce 100644
--- a/src/pulsecore/source.c
+++ b/src/pulsecore/source.c
@@ -56,11 +56,6 @@ struct pa_source_volume_change {
     PA_LLIST_FIELDS(pa_source_volume_change);
 };
 
-struct source_message_set_port {
-    pa_device_port *port;
-    int ret;
-};
-
 struct set_state_data {
     pa_source_state_t state;
     pa_suspend_cause_t suspend_cause;
@@ -246,7 +241,6 @@ pa_source* pa_source_new(
     s->flags = flags;
     s->priority = 0;
     s->suspend_cause = data->suspend_cause;
-    pa_source_set_mixer_dirty(s, false);
     s->name = pa_xstrdup(name);
     s->proplist = pa_proplist_copy(data->proplist);
     s->driver = pa_xstrdup(pa_path_get_filename(data->driver));
@@ -818,11 +812,6 @@ int pa_source_update_status(pa_source*s) {
     return source_set_state(s, pa_source_used_by(s) ? PA_SOURCE_RUNNING : PA_SOURCE_IDLE, 0);
 }
 
-/* Called from any context - must be threadsafe */
-void pa_source_set_mixer_dirty(pa_source *s, bool is_dirty) {
-    pa_atomic_store(&s->mixer_dirty, is_dirty ? 1 : 0);
-}
-
 /* Called from main context */
 int pa_source_suspend(pa_source *s, bool suspend, pa_suspend_cause_t cause) {
     pa_suspend_cause_t merged_cause;
@@ -840,27 +829,6 @@ int pa_source_suspend(pa_source *s, bool suspend, pa_suspend_cause_t cause) {
     else
         merged_cause = s->suspend_cause & ~cause;
 
-    if (!(merged_cause & PA_SUSPEND_SESSION) && (pa_atomic_load(&s->mixer_dirty) != 0)) {
-        /* This might look racy but isn't: If somebody sets mixer_dirty exactly here,
-           it'll be handled just fine. */
-        pa_source_set_mixer_dirty(s, false);
-        pa_log_debug("Mixer is now accessible. Updating alsa mixer settings.");
-        if (s->active_port && s->set_port) {
-            if (s->flags & PA_SOURCE_DEFERRED_VOLUME) {
-                struct source_message_set_port msg = { .port = s->active_port, .ret = 0 };
-                pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_PORT, &msg, 0, NULL) == 0);
-            }
-            else
-                s->set_port(s, s->active_port);
-        }
-        else {
-            if (s->set_mute)
-                s->set_mute(s);
-            if (s->set_volume)
-                s->set_volume(s);
-        }
-    }
-
     if (merged_cause)
         return source_set_state(s, PA_SOURCE_SUSPENDED, merged_cause);
     else
@@ -2338,15 +2306,6 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_
             /* Implementors need to overwrite this implementation! */
             return -1;
 
-        case PA_SOURCE_MESSAGE_SET_PORT:
-
-            pa_assert(userdata);
-            if (s->set_port) {
-                struct source_message_set_port *msg_data = userdata;
-                msg_data->ret = s->set_port(s, msg_data->port);
-            }
-            return 0;
-
         case PA_SOURCE_MESSAGE_UPDATE_VOLUME_AND_MUTE:
             /* This message is sent from IO-thread and handled in main thread. */
             pa_assert_ctl_context();
@@ -2715,7 +2674,6 @@ size_t pa_source_get_max_rewind(pa_source *s) {
 /* Called from main context */
 int pa_source_set_port(pa_source *s, const char *name, bool save) {
     pa_device_port *port;
-    int ret;
 
     pa_source_assert_ref(s);
     pa_assert_ctl_context();
@@ -2736,15 +2694,7 @@ int pa_source_set_port(pa_source *s, const char *name, bool save) {
         return 0;
     }
 
-    if (s->flags & PA_SOURCE_DEFERRED_VOLUME) {
-        struct source_message_set_port msg = { .port = port, .ret = 0 };
-        pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_PORT, &msg, 0, NULL) == 0);
-        ret = msg.ret;
-    }
-    else
-        ret = s->set_port(s, port);
-
-    if (ret < 0)
+    if (s->set_port(s, port) < 0)
         return -PA_ERR_NOENTITY;
 
     pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h
index 96327165..b0fa608d 100644
--- a/src/pulsecore/source.h
+++ b/src/pulsecore/source.h
@@ -116,7 +116,6 @@ struct pa_source {
 
     pa_hashmap *ports;
     pa_device_port *active_port;
-    pa_atomic_t mixer_dirty;
 
     /* The latency offset is inherited from the currently active port */
     int64_t port_latency_offset;
@@ -215,8 +214,8 @@ struct pa_source {
      * thread context. */
     pa_source_cb_t update_requested_latency; /* may be NULL */
 
-    /* Called whenever the port shall be changed. Called from IO
-     * thread if deferred volumes are enabled, and main thread otherwise. */
+    /* Called whenever the port shall be changed. Called from the main
+     * thread. */
     int (*set_port)(pa_source *s, pa_device_port *port); /*ditto */
 
     /* Called to get the list of formats supported by the source, sorted
@@ -294,7 +293,6 @@ typedef enum pa_source_message {
     PA_SOURCE_MESSAGE_GET_FIXED_LATENCY,
     PA_SOURCE_MESSAGE_GET_MAX_REWIND,
     PA_SOURCE_MESSAGE_SET_MAX_REWIND,
-    PA_SOURCE_MESSAGE_SET_PORT,
     PA_SOURCE_MESSAGE_UPDATE_VOLUME_AND_MUTE,
     PA_SOURCE_MESSAGE_SET_PORT_LATENCY_OFFSET,
     PA_SOURCE_MESSAGE_MAX
@@ -415,7 +413,6 @@ bool pa_source_get_mute(pa_source *source, bool force_refresh);
 bool pa_source_update_proplist(pa_source *s, pa_update_mode_t mode, pa_proplist *p);
 
 int pa_source_set_port(pa_source *s, const char *name, bool save);
-void pa_source_set_mixer_dirty(pa_source *s, bool is_dirty);
 
 int pa_source_reconfigure(pa_source *s, pa_sample_spec *spec, bool passthrough);
 

commit 9871b9a4bfff3f9dfcb366652c1f6b3671432fcc
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Mon Mar 19 23:11:04 2018 +0200

    pass pa_suspend_cause_t to set_state_in_io_thread() callbacks
    
    The suspend cause isn't yet used by any of the callbacks. The alsa sink
    and source will use it to sync the mixer when the SESSION suspend cause
    is removed. Currently the syncing is done in pa_sink/source_suspend(),
    and I want to change that, because pa_sink/source_suspend() shouldn't
    have any alsa specific code.

diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c
index e8bb2baa..6551a0b1 100644
--- a/src/modules/alsa/alsa-sink.c
+++ b/src/modules/alsa/alsa-sink.c
@@ -1209,12 +1209,17 @@ static int sink_set_state_in_main_thread_cb(pa_sink *s, pa_sink_state_t new_stat
 }
 
 /* Called from the IO thread. */
-static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state) {
+static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state, pa_suspend_cause_t new_suspend_cause) {
     struct userdata *u;
 
     pa_assert(s);
     pa_assert_se(u = s->userdata);
 
+    /* It may be that only the suspend cause is changing, in which case there's
+     * nothing to do. */
+    if (new_state == s->thread_info.state)
+        return 0;
+
     switch (new_state) {
 
         case PA_SINK_SUSPENDED: {
diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c
index f4d07ae6..94ec999b 100644
--- a/src/modules/alsa/alsa-source.c
+++ b/src/modules/alsa/alsa-source.c
@@ -1064,12 +1064,17 @@ static int source_set_state_in_main_thread_cb(pa_source *s, pa_source_state_t ne
 }
 
 /* Called from the IO thread. */
-static int source_set_state_in_io_thread_cb(pa_source *s, pa_source_state_t new_state) {
+static int source_set_state_in_io_thread_cb(pa_source *s, pa_source_state_t new_state, pa_suspend_cause_t new_suspend_cause) {
     struct userdata *u;
 
     pa_assert(s);
     pa_assert_se(u = s->userdata);
 
+    /* It may be that only the suspend cause is changing, in which case there's
+     * nothing to do. */
+    if (new_state == s->thread_info.state)
+        return 0;
+
     switch (new_state) {
 
         case PA_SOURCE_SUSPENDED: {
diff --git a/src/modules/bluetooth/module-bluez4-device.c b/src/modules/bluetooth/module-bluez4-device.c
index 85eb7986..79d75c11 100644
--- a/src/modules/bluetooth/module-bluez4-device.c
+++ b/src/modules/bluetooth/module-bluez4-device.c
@@ -413,7 +413,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
 }
 
 /* Called from the IO thread. */
-static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state) {
+static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state, pa_suspend_cause_t new_suspend_cause) {
     struct userdata *u;
 
     pa_assert(s);
@@ -487,7 +487,7 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
 }
 
 /* Called from the IO thread. */
-static int source_set_state_in_io_thread_cb(pa_source *s, pa_source_state_t new_state) {
+static int source_set_state_in_io_thread_cb(pa_source *s, pa_source_state_t new_state, pa_suspend_cause_t new_suspend_cause) {
     struct userdata *u;
 
     pa_assert(s);
diff --git a/src/modules/bluetooth/module-bluez5-device.c b/src/modules/bluetooth/module-bluez5-device.c
index 5e189ba2..c3acc1dc 100644
--- a/src/modules/bluetooth/module-bluez5-device.c
+++ b/src/modules/bluetooth/module-bluez5-device.c
@@ -915,7 +915,7 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
 }
 
 /* Called from the IO thread. */
-static int source_set_state_in_io_thread_cb(pa_source *s, pa_source_state_t new_state) {
+static int source_set_state_in_io_thread_cb(pa_source *s, pa_source_state_t new_state, pa_suspend_cause_t new_suspend_cause) {
     struct userdata *u;
 
     pa_assert(s);
@@ -1092,7 +1092,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
 }
 
 /* Called from the IO thread. */
-static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state) {
+static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state, pa_suspend_cause_t new_suspend_cause) {
     struct userdata *u;
 
     pa_assert(s);
diff --git a/src/modules/echo-cancel/module-echo-cancel.c b/src/modules/echo-cancel/module-echo-cancel.c
index 893c41ee..c8065d82 100644
--- a/src/modules/echo-cancel/module-echo-cancel.c
+++ b/src/modules/echo-cancel/module-echo-cancel.c
@@ -514,7 +514,7 @@ static int sink_set_state_in_main_thread_cb(pa_sink *s, pa_sink_state_t state, p
 }
 
 /* Called from the IO thread. */
-static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state) {
+static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state, pa_suspend_cause_t new_suspend_cause) {
     struct userdata *u;
 
     pa_assert(s);
diff --git a/src/modules/module-combine-sink.c b/src/modules/module-combine-sink.c
index bbd416b6..f7649a36 100644
--- a/src/modules/module-combine-sink.c
+++ b/src/modules/module-combine-sink.c
@@ -719,13 +719,18 @@ static int sink_set_state_in_main_thread_cb(pa_sink *sink, pa_sink_state_t state
 }
 
 /* Called from the IO thread. */
-static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state) {
+static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state, pa_suspend_cause_t new_suspend_cause) {
     struct userdata *u;
     bool running;
 
     pa_assert(s);
     pa_assert_se(u = s->userdata);
 
+    /* It may be that only the suspend cause is changing, in which case there's
+     * nothing to do. */
+    if (new_state == s->thread_info.state)
+        return 0;
+
     running = new_state == PA_SINK_RUNNING;
     pa_atomic_store(&u->thread_info.running, running);
 
diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c
index 36029b38..43375999 100644
--- a/src/modules/module-equalizer-sink.c
+++ b/src/modules/module-equalizer-sink.c
@@ -288,7 +288,7 @@ static int sink_set_state_in_main_thread_cb(pa_sink *s, pa_sink_state_t state, p
 }
 
 /* Called from the IO thread. */
-static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state) {
+static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state, pa_suspend_cause_t new_suspend_cause) {
     struct userdata *u;
 
     pa_assert(s);
diff --git a/src/modules/module-esound-sink.c b/src/modules/module-esound-sink.c
index 9fea2da7..3f6b8116 100644
--- a/src/modules/module-esound-sink.c
+++ b/src/modules/module-esound-sink.c
@@ -169,12 +169,17 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
 }
 
 /* Called from the IO thread. */
-static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state) {
+static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state, pa_suspend_cause_t new_suspend_cause) {
     struct userdata *u;
 
     pa_assert(s);
     pa_assert_se(u = s->userdata);
 
+    /* It may be that only the suspend cause is changing, in which case there's
+     * nothing to do. */
+    if (new_state == s->thread_info.state)
+        return 0;
+
     switch (new_state) {
 
         case PA_SINK_SUSPENDED:
diff --git a/src/modules/module-ladspa-sink.c b/src/modules/module-ladspa-sink.c
index de866d96..d677381d 100644
--- a/src/modules/module-ladspa-sink.c
+++ b/src/modules/module-ladspa-sink.c
@@ -395,7 +395,7 @@ static int sink_set_state_in_main_thread_cb(pa_sink *s, pa_sink_state_t state, p
 }
 
 /* Called from the IO thread. */
-static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state) {
+static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state, pa_suspend_cause_t new_suspend_cause) {
     struct userdata *u;
 
     pa_assert(s);
diff --git a/src/modules/module-null-sink.c b/src/modules/module-null-sink.c
index 16b0b687..baaf0647 100644
--- a/src/modules/module-null-sink.c
+++ b/src/modules/module-null-sink.c
@@ -103,7 +103,7 @@ static int sink_process_msg(
 }
 
 /* Called from the IO thread. */
-static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state) {
+static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state, pa_suspend_cause_t new_suspend_cause) {
     struct userdata *u;
 
     pa_assert(s);
diff --git a/src/modules/module-null-source.c b/src/modules/module-null-source.c
index ae67206a..0e4c8d2f 100644
--- a/src/modules/module-null-source.c
+++ b/src/modules/module-null-source.c
@@ -103,7 +103,7 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
 }
 
 /* Called from the IO thread. */
-static int source_set_state_in_io_thread_cb(pa_source *s, pa_source_state_t new_state) {
+static int source_set_state_in_io_thread_cb(pa_source *s, pa_source_state_t new_state, pa_suspend_cause_t new_suspend_cause) {
     struct userdata *u;
 
     pa_assert(s);
diff --git a/src/modules/module-pipe-sink.c b/src/modules/module-pipe-sink.c
index b2378059..fc01206b 100644
--- a/src/modules/module-pipe-sink.c
+++ b/src/modules/module-pipe-sink.c
@@ -136,7 +136,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
 }
 
 /* Called from the IO thread. */
-static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state) {
+static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state, pa_suspend_cause_t new_suspend_cause) {
     struct userdata *u;
 
     pa_assert(s);
diff --git a/src/modules/module-remap-sink.c b/src/modules/module-remap-sink.c
index 56e7a85f..f53b6b15 100644
--- a/src/modules/module-remap-sink.c
+++ b/src/modules/module-remap-sink.c
@@ -115,7 +115,7 @@ static int sink_set_state_in_main_thread(pa_sink *s, pa_sink_state_t state, pa_s
 }
 
 /* Called from the IO thread. */
-static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state) {
+static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state, pa_suspend_cause_t new_suspend_cause) {
     struct userdata *u;
 
     pa_assert(s);
diff --git a/src/modules/module-sine-source.c b/src/modules/module-sine-source.c
index 39fb71ab..803fae5e 100644
--- a/src/modules/module-sine-source.c
+++ b/src/modules/module-sine-source.c
@@ -103,7 +103,7 @@ static int source_process_msg(
 }
 
 /* Called from the IO thread. */
-static int source_set_state_in_io_thread_cb(pa_source *s, pa_source_state_t new_state) {
+static int source_set_state_in_io_thread_cb(pa_source *s, pa_source_state_t new_state, pa_suspend_cause_t new_suspend_cause) {
     struct userdata *u;
 
     pa_assert(s);
diff --git a/src/modules/module-solaris.c b/src/modules/module-solaris.c
index e68f2a93..ef42b3d9 100644
--- a/src/modules/module-solaris.c
+++ b/src/modules/module-solaris.c
@@ -396,12 +396,17 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
 }
 
 /* Called from the IO thread. */
-static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state) {
+static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state, pa_suspend_cause_t new_suspend_cause) {
     struct userdata *u;
 
     pa_assert(s);
     pa_assert_se(u = s->userdata);
 
+    /* It may be that only the suspend cause is changing, in which case there's
+     * nothing to do. */
+    if (new_state == s->thread_info.state)
+        return 0;
+
     switch (new_state) {
 
         case PA_SINK_SUSPENDED:
@@ -457,12 +462,17 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
 }
 
 /* Called from the IO thread. */
-static int source_set_state_in_io_thread_cb(pa_source *s, pa_source_state_t new_state) {
+static int source_set_state_in_io_thread_cb(pa_source *s, pa_source_state_t new_state, pa_suspend_cause_t new_suspend_cause) {
     struct userdata *u;
 
     pa_assert(s);
     pa_assert_se(u = s->userdata);
 
+    /* It may be that only the suspend cause is changing, in which case there's
+     * nothing to do. */
+    if (new_state == s->thread_info.state)
+        return 0;
+
     switch (new_state) {
 
         case PA_SOURCE_SUSPENDED:
diff --git a/src/modules/module-tunnel-sink-new.c b/src/modules/module-tunnel-sink-new.c
index 31390337..802e6a59 100644
--- a/src/modules/module-tunnel-sink-new.c
+++ b/src/modules/module-tunnel-sink-new.c
@@ -434,12 +434,17 @@ static int sink_process_msg_cb(pa_msgobject *o, int code, void *data, int64_t of
 }
 
 /* Called from the IO thread. */
-static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state) {
+static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state, pa_suspend_cause_t new_suspend_cause) {
     struct userdata *u;
 
     pa_assert(s);
     pa_assert_se(u = s->userdata);
 
+    /* It may be that only the suspend cause is changing, in which case there's
+     * nothing to do. */
+    if (new_state == s->thread_info.state)
+        return 0;
+
     if (!u->stream || pa_stream_get_state(u->stream) != PA_STREAM_READY)
         return 0;
 
diff --git a/src/modules/module-tunnel-source-new.c b/src/modules/module-tunnel-source-new.c
index d0a5414a..b41f53e2 100644
--- a/src/modules/module-tunnel-source-new.c
+++ b/src/modules/module-tunnel-source-new.c
@@ -433,12 +433,17 @@ static int source_process_msg_cb(pa_msgobject *o, int code, void *data, int64_t
 }
 
 /* Called from the IO thread. */
-static int source_set_state_in_io_thread_cb(pa_source *s, pa_source_state_t new_state) {
+static int source_set_state_in_io_thread_cb(pa_source *s, pa_source_state_t new_state, pa_suspend_cause_t new_suspend_cause) {
     struct userdata *u;
 
     pa_assert(s);
     pa_assert_se(u = s->userdata);
 
+    /* It may be that only the suspend cause is changing, in which case there's
+     * nothing to do. */
+    if (new_state == s->thread_info.state)
+        return 0;
+
     if (!u->stream || pa_stream_get_state(u->stream) != PA_STREAM_READY)
         return 0;
 
diff --git a/src/modules/module-virtual-sink.c b/src/modules/module-virtual-sink.c
index 68ad2007..e1345523 100644
--- a/src/modules/module-virtual-sink.c
+++ b/src/modules/module-virtual-sink.c
@@ -127,7 +127,7 @@ static int sink_set_state_in_main_thread_cb(pa_sink *s, pa_sink_state_t state, p
 }
 
 /* Called from the IO thread. */
-static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state) {
+static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state, pa_suspend_cause_t new_suspend_cause) {
     struct userdata *u;
 
     pa_assert(s);
diff --git a/src/modules/module-virtual-surround-sink.c b/src/modules/module-virtual-surround-sink.c
index 7c5e246c..2c3d54ba 100644
--- a/src/modules/module-virtual-surround-sink.c
+++ b/src/modules/module-virtual-surround-sink.c
@@ -155,7 +155,7 @@ static int sink_set_state_in_main_thread_cb(pa_sink *s, pa_sink_state_t state, p
 }
 
 /* Called from the IO thread. */
-static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state) {
+static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state, pa_suspend_cause_t new_suspend_cause) {
     struct userdata *u;
 
     pa_assert(s);
diff --git a/src/modules/oss/module-oss.c b/src/modules/oss/module-oss.c
index d2551bcf..42a6e3b0 100644
--- a/src/modules/oss/module-oss.c
+++ b/src/modules/oss/module-oss.c
@@ -666,7 +666,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
 }
 
 /* Called from the IO thread. */
-static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state) {
+static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state, pa_suspend_cause_t new_suspend_cause) {
     struct userdata *u;
     bool do_trigger = false;
     bool quick = true;
@@ -674,6 +674,11 @@ static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state)
     pa_assert(s);
     pa_assert_se(u = s->userdata);
 
+    /* It may be that only the suspend cause is changing, in which case there's
+     * nothing to do. */
+    if (new_state == s->thread_info.state)
+        return 0;
+
     switch (new_state) {
 
         case PA_SINK_SUSPENDED:
@@ -749,7 +754,7 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
 }
 
 /* Called from the IO thread. */
-static int source_set_state_in_io_thread_cb(pa_source *s, pa_source_state_t new_state) {
+static int source_set_state_in_io_thread_cb(pa_source *s, pa_source_state_t new_state, pa_suspend_cause_t new_suspend_cause) {
     struct userdata *u;
     bool do_trigger = false;
     bool quick = true;
@@ -757,6 +762,11 @@ static int source_set_state_in_io_thread_cb(pa_source *s, pa_source_state_t new_
     pa_assert(s);
     pa_assert_se(u = s->userdata);
 
+    /* It may be that only the suspend cause is changing, in which case there's
+     * nothing to do. */
+    if (new_state == s->thread_info.state)
+        return 0;
+
     switch (new_state) {
 
         case PA_SOURCE_SUSPENDED:
diff --git a/src/modules/raop/raop-sink.c b/src/modules/raop/raop-sink.c
index baa34664..818cdfe0 100644
--- a/src/modules/raop/raop-sink.c
+++ b/src/modules/raop/raop-sink.c
@@ -221,12 +221,17 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
 }
 
 /* Called from the IO thread. */
-static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state) {
+static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state, pa_suspend_cause_t new_suspend_cause) {
     struct userdata *u;
 
     pa_assert(s);
     pa_assert_se(u = s->userdata);
 
+    /* It may be that only the suspend cause is changing, in which case there's
+     * nothing to do. */
+    if (new_state == s->thread_info.state)
+        return 0;
+
     switch (new_state) {
         case PA_SINK_SUSPENDED:
             pa_log_debug("RAOP: SUSPENDED");
diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index 2c933493..2174354f 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -69,6 +69,11 @@ struct sink_message_set_port {
     int ret;
 };
 
+struct set_state_data {
+    pa_sink_state_t state;
+    pa_suspend_cause_t suspend_cause;
+};
+
 static void sink_free(pa_object *s);
 
 static void pa_sink_volume_change_push(pa_sink *s);
@@ -429,19 +434,49 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state, pa_suspend_cause_t
      * current approach of not setting any suspend cause works well enough. */
 
     if (s->set_state_in_main_thread) {
-        ret = s->set_state_in_main_thread(s, state, suspend_cause);
-        /* set_state_in_main_thread() is allowed to fail only when resuming. */
-        pa_assert(ret >= 0 || resuming);
+        if ((ret = s->set_state_in_main_thread(s, state, suspend_cause)) < 0) {
+            /* set_state_in_main_thread() is allowed to fail only when resuming. */
+            pa_assert(resuming);
+
+            /* If resuming fails, we set the state to SUSPENDED and
+             * suspend_cause to 0. */
+            state = PA_SINK_SUSPENDED;
+            suspend_cause = 0;
+            state_changed = false;
+            suspend_cause_changed = suspend_cause != s->suspend_cause;
+            resuming = false;
+
+            /* We know the state isn't changing. If the suspend cause isn't
+             * changing either, then there's nothing more to do. */
+            if (!suspend_cause_changed)
+                return ret;
+        }
     }
 
-    if (ret >= 0 && s->asyncmsgq && state_changed)
-        if ((ret = pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL)) < 0) {
+    if (s->asyncmsgq) {
+        struct set_state_data data = { .state = state, .suspend_cause = suspend_cause };
+
+        if ((ret = pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_STATE, &data, 0, NULL)) < 0) {
             /* SET_STATE is allowed to fail only when resuming. */
             pa_assert(resuming);
 
             if (s->set_state_in_main_thread)
                 s->set_state_in_main_thread(s, PA_SINK_SUSPENDED, 0);
+
+            /* If resuming fails, we set the state to SUSPENDED and
+             * suspend_cause to 0. */
+            state = PA_SINK_SUSPENDED;
+            suspend_cause = 0;
+            state_changed = false;
+            suspend_cause_changed = suspend_cause != s->suspend_cause;
+            resuming = false;
+
+            /* We know the state isn't changing. If the suspend cause isn't
+             * changing either, then there's nothing more to do. */
+            if (!suspend_cause_changed)
+                return ret;
         }
+    }
 
     if (suspend_cause_changed) {
         char old_cause_buf[PA_SUSPEND_CAUSE_TO_STRING_BUF_SIZE];
@@ -452,9 +487,6 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state, pa_suspend_cause_t
         s->suspend_cause = suspend_cause;
     }
 
-    if (ret < 0)
-        goto finish;
-
     if (state_changed) {
         pa_log_debug("%s: state: %s -> %s", s->name, pa_sink_state_to_string(s->state), pa_sink_state_to_string(state));
         s->state = state;
@@ -481,7 +513,6 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state, pa_suspend_cause_t
                 i->suspend(i, state == PA_SINK_SUSPENDED);
     }
 
-finish:
     if ((suspending || resuming || suspend_cause_changed) && s->monitor_source && state != PA_SINK_UNLINKED)
         pa_source_sync_suspend(s->monitor_source);
 
@@ -2846,19 +2877,19 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
             return 0;
 
         case PA_SINK_MESSAGE_SET_STATE: {
-
+            struct set_state_data *data = userdata;
             bool suspend_change =
-                (s->thread_info.state == PA_SINK_SUSPENDED && PA_SINK_IS_OPENED(PA_PTR_TO_UINT(userdata))) ||
-                (PA_SINK_IS_OPENED(s->thread_info.state) && PA_PTR_TO_UINT(userdata) == PA_SINK_SUSPENDED);
+                (s->thread_info.state == PA_SINK_SUSPENDED && PA_SINK_IS_OPENED(data->state)) ||
+                (PA_SINK_IS_OPENED(s->thread_info.state) && data->state == PA_SINK_SUSPENDED);
 
             if (s->set_state_in_io_thread) {
                 int r;
 
-                if ((r = s->set_state_in_io_thread(s, PA_PTR_TO_UINT(userdata))) < 0)
+                if ((r = s->set_state_in_io_thread(s, data->state, data->suspend_cause)) < 0)
                     return r;
             }
 
-            s->thread_info.state = PA_PTR_TO_UINT(userdata);
+            s->thread_info.state = data->state;
 
             if (s->thread_info.state == PA_SINK_SUSPENDED) {
                 s->thread_info.rewind_nbytes = 0;
diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h
index e1ea5249..3e9191d2 100644
--- a/src/pulsecore/sink.h
+++ b/src/pulsecore/sink.h
@@ -148,7 +148,7 @@ struct pa_sink {
      * are updated only after all the callback calls. In case of failure, the
      * state is set to SUSPENDED and the suspend cause is set to 0. */
     int (*set_state_in_main_thread)(pa_sink *s, pa_sink_state_t state, pa_suspend_cause_t suspend_cause); /* may be NULL */
-    int (*set_state_in_io_thread)(pa_sink *s, pa_sink_state_t state); /* may be NULL */
+    int (*set_state_in_io_thread)(pa_sink *s, pa_sink_state_t state, pa_suspend_cause_t suspend_cause); /* may be NULL */
 
     /* Sink drivers that support hardware volume may set this
      * callback. This is called when the current volume needs to be
diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
index dd56eb08..e04463c6 100644
--- a/src/pulsecore/source.c
+++ b/src/pulsecore/source.c
@@ -61,6 +61,11 @@ struct source_message_set_port {
     int ret;
 };
 
+struct set_state_data {
+    pa_source_state_t state;
+    pa_suspend_cause_t suspend_cause;
+};
+
 static void source_free(pa_object *o);
 
 static void pa_source_volume_change_push(pa_source *s);
@@ -383,19 +388,49 @@ static int source_set_state(pa_source *s, pa_source_state_t state, pa_suspend_ca
      * current approach of not setting any suspend cause works well enough. */
 
     if (s->set_state_in_main_thread) {
-        ret = s->set_state_in_main_thread(s, state, suspend_cause);
-        /* set_state_in_main_thread() is allowed to fail only when resuming. */
-        pa_assert(ret >= 0 || resuming);
+        if ((ret = s->set_state_in_main_thread(s, state, suspend_cause)) < 0) {
+            /* set_state_in_main_thread() is allowed to fail only when resuming. */
+            pa_assert(resuming);
+
+            /* If resuming fails, we set the state to SUSPENDED and
+             * suspend_cause to 0. */
+            state = PA_SOURCE_SUSPENDED;
+            suspend_cause = 0;
+            state_changed = false;
+            suspend_cause_changed = suspend_cause != s->suspend_cause;
+            resuming = false;
+
+            /* We know the state isn't changing. If the suspend cause isn't
+             * changing either, then there's nothing more to do. */
+            if (!suspend_cause_changed)
+                return ret;
+        }
     }
 
-    if (ret >= 0 && s->asyncmsgq && state_changed)
-        if ((ret = pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL)) < 0) {
+    if (s->asyncmsgq) {
+        struct set_state_data data = { .state = state, .suspend_cause = suspend_cause };
+
+        if ((ret = pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_STATE, &data, 0, NULL)) < 0) {
             /* SET_STATE is allowed to fail only when resuming. */
             pa_assert(resuming);
 
             if (s->set_state_in_main_thread)
                 s->set_state_in_main_thread(s, PA_SOURCE_SUSPENDED, 0);
+
+            /* If resuming fails, we set the state to SUSPENDED and
+             * suspend_cause to 0. */
+            state = PA_SOURCE_SUSPENDED;
+            suspend_cause = 0;
+            state_changed = false;
+            suspend_cause_changed = suspend_cause != s->suspend_cause;
+            resuming = false;
+
+            /* We know the state isn't changing. If the suspend cause isn't
+             * changing either, then there's nothing more to do. */
+            if (!suspend_cause_changed)
+                return ret;
         }
+    }
 
     if (suspend_cause_changed) {
         char old_cause_buf[PA_SUSPEND_CAUSE_TO_STRING_BUF_SIZE];
@@ -406,9 +441,6 @@ static int source_set_state(pa_source *s, pa_source_state_t state, pa_suspend_ca
         s->suspend_cause = suspend_cause;
     }
 
-    if (ret < 0)
-        goto finish;
-
     if (state_changed) {
         pa_log_debug("%s: state: %s -> %s", s->name, pa_source_state_to_string(s->state), pa_source_state_to_string(state));
         s->state = state;
@@ -435,7 +467,6 @@ static int source_set_state(pa_source *s, pa_source_state_t state, pa_suspend_ca
                 o->suspend(o, state == PA_SOURCE_SUSPENDED);
     }
 
-finish:
     return ret;
 }
 
@@ -2220,19 +2251,19 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_
             return 0;
 
         case PA_SOURCE_MESSAGE_SET_STATE: {
-
+            struct set_state_data *data = userdata;
             bool suspend_change =
-                (s->thread_info.state == PA_SOURCE_SUSPENDED && PA_SOURCE_IS_OPENED(PA_PTR_TO_UINT(userdata))) ||
-                (PA_SOURCE_IS_OPENED(s->thread_info.state) && PA_PTR_TO_UINT(userdata) == PA_SOURCE_SUSPENDED);
+                (s->thread_info.state == PA_SOURCE_SUSPENDED && PA_SOURCE_IS_OPENED(data->state)) ||
+                (PA_SOURCE_IS_OPENED(s->thread_info.state) && data->state == PA_SOURCE_SUSPENDED);
 
             if (s->set_state_in_io_thread) {
                 int r;
 
-                if ((r = s->set_state_in_io_thread(s, PA_PTR_TO_UINT(userdata))) < 0)
+                if ((r = s->set_state_in_io_thread(s, data->state, data->suspend_cause)) < 0)
                     return r;
             }
 
-            s->thread_info.state = PA_PTR_TO_UINT(userdata);
+            s->thread_info.state = data->state;
 
             if (suspend_change) {
                 pa_source_output *o;
diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h
index c4fda796..96327165 100644
--- a/src/pulsecore/source.h
+++ b/src/pulsecore/source.h
@@ -149,7 +149,7 @@ struct pa_source {
      * are updated only after all the callback calls. In case of failure, the
      * state is set to SUSPENDED and the suspend cause is set to 0. */
     int (*set_state_in_main_thread)(pa_source *s, pa_source_state_t state, pa_suspend_cause_t suspend_cause); /* may be NULL */
-    int (*set_state_in_io_thread)(pa_source *s, pa_source_state_t state); /* may be NULL */
+    int (*set_state_in_io_thread)(pa_source *s, pa_source_state_t state, pa_suspend_cause_t suspend_cause); /* may be NULL */
 
     /* Called when the volume is queried. Called from main loop
      * context. If this is NULL a PA_SOURCE_MESSAGE_GET_VOLUME message



More information about the pulseaudio-commits mailing list