alsa: fix race when updating the eventfd

The eventfd is read/written from/to the data thread and the main thread
concurrently with the update_active() function.

Use an atomic compare and swap to make this update atomic and avoid an
inconsistency between the active boolean and the eventfd. This could
result in the eventfd being unsignaled while the active flag was true
and the application receiving a timeout and XRun in its poll loop.

Fixes #3711
This commit is contained in:
Wim Taymans 2024-04-16 09:30:23 +02:00
parent 232f4f01e4
commit 2b11efdf3b

View file

@ -56,9 +56,10 @@ typedef struct {
unsigned int draining:1;
unsigned int xrun_detected:1;
unsigned int hw_params_changed:1;
unsigned int active:1;
unsigned int negotiated:1;
bool active;
snd_pcm_uframes_t hw_ptr;
snd_pcm_uframes_t boundary;
snd_pcm_uframes_t min_avail;
@ -93,8 +94,9 @@ static int update_active(snd_pcm_ioplug_t *io)
{
snd_pcm_pipewire_t *pw = io->private_data;
snd_pcm_sframes_t avail;
bool active;
bool active, old;
retry:
avail = snd_pcm_ioplug_avail(io, pw->hw_ptr, io->appl_ptr);
if (pw->error > 0) {
@ -112,7 +114,8 @@ static int update_active(snd_pcm_ioplug_t *io)
else {
active = false;
}
if (pw->active != active) {
old = SPA_ATOMIC_LOAD(pw->active);
if (old != active) {
uint64_t val;
pw_log_trace("%p: avail:%lu min-avail:%lu state:%s hw:%lu appl:%lu active:%d->%d state:%s",
@ -120,11 +123,13 @@ static int update_active(snd_pcm_ioplug_t *io)
pw->hw_ptr, io->appl_ptr, pw->active, active,
snd_pcm_state_name(io->state));
pw->active = active;
if (active)
spa_system_eventfd_write(pw->system, io->poll_fd, 1);
else
spa_system_eventfd_read(pw->system, io->poll_fd, &val);
if (!SPA_ATOMIC_CAS(pw->active, old, active))
goto retry;
}
return active;
}