From a01a33aa7500e80c0ff40c84be7c0c5ec2db193d Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 3 Apr 2024 11:18:46 +0200 Subject: [PATCH] impl-node: improve xrun handling To check if the driver was not completed, check the pending state instead of the status. This is more correct and we can do this a little smarter by swapping the current state with 0 atomically. When we detect a non-zero pending state, wake up the driver node. Because we atomically swapped 0, we are the only ones doing this and it will make the node process whatever was in the input ports instead of underrunning. Running 2 apps outputting to a sink and pausing one will still play the output of the other one, this way. See #3509 Fixes #3937 --- src/pipewire/impl-node.c | 15 +++++++++++---- src/pipewire/private.h | 1 + 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/pipewire/impl-node.c b/src/pipewire/impl-node.c index 0f3cd991e..7584383c2 100644 --- a/src/pipewire/impl-node.c +++ b/src/pipewire/impl-node.c @@ -1209,6 +1209,12 @@ static inline uint64_t get_time_ns(struct spa_system *system) return SPA_TIMESPEC_TO_NSEC(&ts); } +static inline void wake_target(struct pw_node_target *t) +{ + if (SPA_UNLIKELY(spa_system_eventfd_write(t->system, t->fd, 1) < 0)) + pw_log_warn("%p: write failed %m", t->node); +} + /* called from data-loop decrement the dependency counter of the target and when * there are no more dependencies, trigger the node. */ static inline void trigger_target(struct pw_node_target *t, uint64_t nsec) @@ -1222,8 +1228,7 @@ static inline void trigger_target(struct pw_node_target *t, uint64_t nsec) if (pw_node_activation_state_dec(state)) { a->status = PW_NODE_ACTIVATION_TRIGGERED; a->signal_time = nsec; - if (SPA_UNLIKELY(spa_system_eventfd_write(t->system, t->fd, 1) < 0)) - pw_log_warn("%p: write failed %m", t->node); + wake_target(t); } } @@ -1838,12 +1843,14 @@ static int node_ready(void *data, int status) int sync_type, all_ready, update_sync, target_sync; uint32_t owner[2], reposition_owner; uint64_t min_timeout = UINT64_MAX; + int32_t pending; - if (SPA_UNLIKELY(a->status != PW_NODE_ACTIVATION_FINISHED)) { + if (SPA_UNLIKELY((pending = pw_node_activation_state_xchg(state)) != 0)) { pw_log_debug("(%s-%u) graph not finished: state:%p quantum:%"PRIu64 " pending %d/%d", node->name, node->info.id, state, a->position.clock.duration, - state->pending, state->required); + pending, state->required); + wake_target(&node->rt.target); check_states(node, nsec); pw_impl_node_rt_emit_incomplete(node); } diff --git a/src/pipewire/private.h b/src/pipewire/private.h index 6f463f6d2..0a6c75501 100644 --- a/src/pipewire/private.h +++ b/src/pipewire/private.h @@ -526,6 +526,7 @@ static inline void pw_node_activation_state_reset(struct pw_node_activation_stat } #define pw_node_activation_state_dec(s) (SPA_ATOMIC_DEC(s->pending) == 0) +#define pw_node_activation_state_xchg(s) SPA_ATOMIC_XCHG(s->pending, 0) struct pw_node_target { struct spa_list link;