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
This commit is contained in:
Wim Taymans 2024-04-03 11:18:46 +02:00
parent 208a343d9f
commit a01a33aa75
2 changed files with 12 additions and 4 deletions

View file

@ -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);
}

View file

@ -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;