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 17a2c21573
commit 98f9529147

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