Skip to content

alsa: refactor and enhance worker loop#1122

Open
roderickvd wants to merge 8 commits intomasterfrom
fix/alsa-capture-overruns
Open

alsa: refactor and enhance worker loop#1122
roderickvd wants to merge 8 commits intomasterfrom
fix/alsa-capture-overruns

Conversation

@roderickvd
Copy link
Member

@roderickvd roderickvd commented Mar 3, 2026

Fixes

  • Capture overrun recovery (ALSA: invalid handling of capture overruns #730): snd_pcm_start() was not called after snd_pcm_prepare() when recovering a capture stream from an underrun.

  • POLLERR mishandled as error: POLLERR from snd_pcm_poll_descriptors_revents() signals a pending underrun. It was being returned as BackendSpecificError, causing spurious error callbacks and skipping recovery.

  • poll() timeout reported as error: poll() returning 0 (timeout or spurious wakeup) was fired as a BackendSpecificError on every timeout .

  • Device disconnection looping: POLLHUP/POLLNVAL were not handled. The worker now stops with StreamError::DeviceNotAvailable on device removal.

  • SIGPIPE on early worker exit: If the worker exited early (e.g. on DeviceNotAvailable) before Stream::drop() called wakeup(), the write to the self-pipe's closed read end raised SIGPIPE

  • EINTR panic in self-pipe paths: write()/read() on the self-pipe could return -1 with EINTR on signal delivery, causing assert_eq!(ret, 8) to panic.

Added

Suspend/resume support: ESTRPIPE from avail() or writei() (system suspend) is now handled.

Changed

The internal PollDescriptorsFlow enum and poll_descriptors_and_prepare_buffer function have
been refactored for simplicity and likeness to Ready/Pending vocabulary.

Fixes #730

POLLERR from snd_pcm_poll_descriptors_revents() signals an xrun but was
being returned as a BackendSpecificError, causing the poll loop to spin
indefinitely. It now falls through to avail(), which returns EPIPE and
triggers xrun recovery.

Capture streams also require an explicit snd_pcm_start() after
snd_pcm_prepare() to re-enter SND_PCM_STATE_RUNNING; without it the
stream stalled in PREPARED and poll() timed out repeatedly.

POLLHUP/POLLNVAL now stop the stream with StreamError::DeviceNotAvailable
instead of looping and poll() returning 0 (timeout/spurious) is now
treated as Continue rather than an error.

Fixes #730

This comment was marked as outdated.

@roderickvd
Copy link
Member Author

The Copilot review seems a valid concern and a pre-existing fragility. I'll think about how to address it.

TriggerReceiver is now wrapped in Arc and a clone is stored in Stream
alongside the sender. Worker threads take their own Arc clone, so
dropping the worker no longer closes the read end of the pipe.

This means wakeup() in Stream::drop() always writes to an open pipe,
even when the worker exited early due to a device error, eliminating
the SIGPIPE that would otherwise be raised.
@roderickvd roderickvd changed the title fix(alsa): correct handling of capture overruns alsa: refactor and enhance worker loop Mar 6, 2026
@roderickvd roderickvd requested a review from Copilot March 6, 2026 20:20
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@roderickvd
Copy link
Member Author

This PR has grown quite a bit beyond its original #730 scope so I updated the PR title and text.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ALSA: invalid handling of capture overruns

2 participants