When a filter sink is moving, it's not connected to any master sink, and therefore it's not connected to any IO thread either. In this situation trying to move a stream that is connected to the filter sink is likely to result in crashing, because starting the move involves sending a message to the IO thread. Sometimes this works by accident (the asyncmsgq of the filter sink still points to the old master sink's asyncmsgq), but we really should never attempt it. This patch blocks all moves where the moving stream is connected to a filter sink that itself is in the middle of a move. BugLink: https://bugs.freedesktop.org/show_bug.cgi?id=100277 --- This is an old patch that was not applied on the first submission, because it was not necessary for fixing the bug at hand at that time. Here's the v1 (there are no changes in the code): https://patchwork.freedesktop.org/patch/68741/ Changes in v2: - rewrote the commit message src/pulsecore/sink-input.c | 26 ++++++++++++++++++++++++++ src/pulsecore/source-output.c | 27 +++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index 2ceed412d..4155b69a5 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -1627,6 +1627,22 @@ static bool find_filter_sink_input(pa_sink_input *target, pa_sink *s) { return false; } +static bool is_filter_sink_moving(pa_sink_input *i) { + pa_sink *sink = i->sink; + + if (!sink) + return false; + + while (sink->input_to_master) { + sink = sink->input_to_master->sink; + + if (!sink) + return true; + } + + return false; +} + /* Called from main context */ bool pa_sink_input_may_move_to(pa_sink_input *i, pa_sink *dest) { pa_sink_input_assert_ref(i); @@ -1649,6 +1665,16 @@ bool pa_sink_input_may_move_to(pa_sink_input *i, pa_sink *dest) { return false; } + /* If this sink input is connected to a filter sink that itself is moving, + * then don't allow the move. Moving requires sending a message to the IO + * thread of the old sink, and if the old sink is a filter sink that is + * moving, there's no IO thread associated to the old sink. */ + if (is_filter_sink_moving(i)) { + pa_log_debug("Can't move input from filter sink %s, because the filter sink itself is currently moving.", + i->sink->name); + return false; + } + if (pa_idxset_size(dest->inputs) >= PA_MAX_INPUTS_PER_SINK) { pa_log_warn("Failed to move sink input: too many inputs per sink."); return false; diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c index fa32a5666..a4c99af0e 100644 --- a/src/pulsecore/source-output.c +++ b/src/pulsecore/source-output.c @@ -1265,6 +1265,22 @@ static bool find_filter_source_output(pa_source_output *target, pa_source *s) { return false; } +static bool is_filter_source_moving(pa_source_output *o) { + pa_source *source = o->source; + + if (!source) + return false; + + while (source->output_from_master) { + source = source->output_from_master->source; + + if (!source) + return true; + } + + return false; +} + /* Called from main context */ bool pa_source_output_may_move_to(pa_source_output *o, pa_source *dest) { pa_source_output_assert_ref(o); @@ -1286,6 +1302,17 @@ bool pa_source_output_may_move_to(pa_source_output *o, pa_source *dest) { return false; } + /* If this source output is connected to a filter source that itself is + * moving, then don't allow the move. Moving requires sending a message to + * the IO thread of the old source, and if the old source is a filter + * source that is moving, there's no IO thread associated to the old + * source. */ + if (is_filter_source_moving(o)) { + pa_log_debug("Can't move output from filter source %s, because the filter source itself is currently moving.", + o->source->name); + return false; + } + if (pa_idxset_size(dest->outputs) >= PA_MAX_OUTPUTS_PER_SOURCE) { pa_log_warn("Failed to move source output: too many outputs per source."); return false; -- 2.11.0