From 839383d0dde8016f64c6b596917b971b4d1c37b0 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 9 Sep 2025 15:14:36 +0200 Subject: [PATCH] impl-node: improve the node unprepare function do_node_unprepare runs in both the server and the client when a node is stopped. On the server size, set the status to FINISHED and trigger any targets. This ensures the node will not be scheduled in this cycle anymore. We have to do this because we can't know if the node is still alive or not. When the client receives the stop message, it will unprepare and set the status to INACTIVE. This ensures the driver will no longer trigger the node. If the server didn't already trigger the targets, do this in the remote node then. This avoid a race where both the client and the server are setting the status and if the INACTIVE state is set by the server, it might stall processing of the client. Fixes #4840 --- src/pipewire/impl-node.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/pipewire/impl-node.c b/src/pipewire/impl-node.c index e5636d561..89bb03686 100644 --- a/src/pipewire/impl-node.c +++ b/src/pipewire/impl-node.c @@ -219,17 +219,21 @@ do_node_unprepare(struct spa_loop *loop, bool async, uint32_t seq, { struct pw_impl_node *this = user_data; struct pw_node_target *t; - int old_state; + int old_state, new_state; uint64_t trigger = 0; pw_log_trace("%p: unprepare %d remote:%d exported:%d", this, this->rt.prepared, this->remote, this->exported); - /* We mark ourself as finished now, this will avoid going further into the process loop - * in case our fd was ready (removing ourselfs from the loop should avoid that as well). - * If we were supposed to be scheduled make sure we continue the graph for the peers we - * were supposed to trigger */ - old_state = SPA_ATOMIC_XCHG(this->rt.target.activation->status, PW_NODE_ACTIVATION_INACTIVE); + /* The remote client will INACTIVE itself and remove itself from the loop to avoid + * being scheduled. + * The server will mark remote nodes as FINISHED. This will make sure the node will not + * trigger the peers anymore when it will stop because we do that on the server side + * because the client might simply be dead and not able to resume anything. + */ + new_state = this->remote ? PW_NODE_ACTIVATION_FINISHED : PW_NODE_ACTIVATION_INACTIVE; + + old_state = SPA_ATOMIC_XCHG(this->rt.target.activation->status, new_state); if (PW_NODE_ACTIVATION_PENDING_TRIGGER(old_state)) trigger = get_time_ns(this->rt.target.system);