pass pa_suspend_cause_t to set_state_in_io_thread() callbacks
authorTanu Kaskinen <tanuk@iki.fi>
Mon, 19 Mar 2018 21:11:04 +0000 (23:11 +0200)
committerTanu Kaskinen <tanuk@iki.fi>
Tue, 20 Mar 2018 11:00:44 +0000 (13:00 +0200)
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.

25 files changed:
src/modules/alsa/alsa-sink.c
src/modules/alsa/alsa-source.c
src/modules/bluetooth/module-bluez4-device.c
src/modules/bluetooth/module-bluez5-device.c
src/modules/echo-cancel/module-echo-cancel.c
src/modules/module-combine-sink.c
src/modules/module-equalizer-sink.c
src/modules/module-esound-sink.c
src/modules/module-ladspa-sink.c
src/modules/module-null-sink.c
src/modules/module-null-source.c
src/modules/module-pipe-sink.c
src/modules/module-remap-sink.c
src/modules/module-sine-source.c
src/modules/module-solaris.c
src/modules/module-tunnel-sink-new.c
src/modules/module-tunnel-source-new.c
src/modules/module-virtual-sink.c
src/modules/module-virtual-surround-sink.c
src/modules/oss/module-oss.c
src/modules/raop/raop-sink.c
src/pulsecore/sink.c
src/pulsecore/sink.h
src/pulsecore/source.c
src/pulsecore/source.h

index 9e33537..3b962b9 100644 (file)
@@ -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: {
index 312b259..69da88d 100644 (file)
@@ -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: {
index 85eb798..79d75c1 100644 (file)
@@ -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);
index 5e189ba..c3acc1d 100644 (file)
@@ -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);
index 893c41e..c8065d8 100644 (file)
@@ -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);
index bbd416b..f7649a3 100644 (file)
@@ -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);
 
index 36029b3..4337599 100644 (file)
@@ -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);
index 9fea2da..3f6b811 100644 (file)
@@ -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:
index de866d9..d677381 100644 (file)
@@ -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);
index 16b0b68..baaf064 100644 (file)
@@ -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);
index ae67206..0e4c8d2 100644 (file)
@@ -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);
index b237805..fc01206 100644 (file)
@@ -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);
index 56e7a85..f53b6b1 100644 (file)
@@ -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);
index 39fb71a..803fae5 100644 (file)
@@ -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);
index e68f2a9..ef42b3d 100644 (file)
@@ -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:
index 3139033..802e6a5 100644 (file)
@@ -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;
 
index d0a5414..b41f53e 100644 (file)
@@ -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;
 
index 68ad200..e134552 100644 (file)
@@ -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);
index 7c5e246..2c3d54b 100644 (file)
@@ -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);
index d2551bc..42a6e3b 100644 (file)
@@ -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:
index baa3466..818cdfe 100644 (file)
@@ -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");
index 2c93349..2174354 100644 (file)
@@ -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;
index e1ea524..3e9191d 100644 (file)
@@ -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
index dd56eb0..e04463c 100644 (file)
@@ -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;
index c4fda79..9632716 100644 (file)
@@ -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