Skip to content

alsa: restart non-MMAP capture after XRUN recovery - #1194

Open
whatisyourname0 wants to merge 4 commits into
PortAudio:masterfrom
whatisyourname0:fix/alsa-capture-xrun-restart
Open

whatisyourname0 wants to merge 4 commits into
PortAudio:masterfrom
whatisyourname0:fix/alsa-capture-xrun-restart

Conversation

@whatisyourname0

@whatisyourname0 whatisyourname0 commented Oct 17, 2026 •

Copy link
Copy Markdown

Fixes #1195.

A non-MMAP capture stream can stop delivering callbacks after an overrun while PortAudio still reports it as active. snd_pcm_recover() prepares the capture PCM, but PaAlsaStream_HandleXrun() returns without starting it. The callback thread then waits for input from a PCM that remains PREPARED.

Start the recovered capture PCM when it is PREPARED, unlinked, and no full ALSA restart is already pending. If that start fails, use the existing full restart path. Persistent start failures retain the existing error handling. Playback, linked PCM startup and MMAP recovery policies are unchanged; there is no public API change.

The optional qa/alsa-xrun reproducer is now a single C executable. It uses shared PortAudio and ALSA with a test-only symbol-interposition fixture to exercise non-MMAP capture on a null PCM. The README includes native compile commands and expected baseline/candidate outcomes. It needs no Docker, Python, separate preload library or audio hardware. The previous Docker/Python harness was removed in response to review.

Validation

Base: 7c45fed9322bd86b5dd0da4547b15d9ac3b9b0ac (master at preparation time).

  • Blocking C tests: capture and unlinked duplex, successful recovery, one start failure, persistent start failure and one recovery failure. The candidate met the expected outcomes across 24 open/close cycles. MMAP comparison outcomes were unchanged, including propagation of a single start failure.
  • Earlier sounddevice callback tests: the baseline stopped at five callbacks with the stream still active. The candidate resumed to 100 callbacks after recoverable faults, repeated across three opens per case. Persistent EIO ended the stream and invoked the finished callback.
  • Physical input: a MacBook Pro internal microphone through Apple VZ and an experimental virtio-snd guest kernel. A one-second callback delay caused a real overrun. The baseline stopped delivering callbacks and teardown stalled; the candidate recovered and reopened successfully in three trials.
  • On that physical path, a single injected start EIO after a real overrun recovered through the full restart in three trials. Persistent start EIO ended the stream in three trials. Three subsequent recordings succeeded after removing the fault.
  • Standalone C reproducer: all ten baseline/candidate outcomes matched expectations. The baseline stalls after callback five in the XRUN cases; the candidate completes three open/close cycles in every mode, including the expected inactive-stream outcome for persistent EIO. The C source builds with -Wall -Wextra -Werror. Its real-input delayed-callback mode was subsequently compared against both libraries using MOTU UltraLite-mk5 through Apple VZ and the same experimental virtio-snd guest kernel. The baseline stalled at five callbacks; the candidate passed three overrun/close/reopen trials and three following normal opens. A separate signal-inspection variant confirmed nonzero audio after recovery on a fresh VM boot. See ALSA non-MMAP capture stops delivering callbacks after successful XRUN recovery #1195 for results and the distinction from direct Linux USB audio testing.
  • Repository whitespace checks and Linux builds passed. Existing unused ALSA library-variable warnings remain in the warning-enabled builds.

The callback block size was 4,096 frames at 48 kHz; this is not a fixed hardware buffer size. Synthetic XRUN reports and real overruns are separate tests. The physical tests used an experimental guest driver and short recordings, so they do not establish USB-device, linked-duplex, hot-unplug or long-duration coverage. Audio lost during an overrun is not restored.

Related reports

#930 has a similar symptom, but its root cause has not been established. #1037 / #1038 addressed error-handler loops; this change addresses successful recovery leaving capture PREPARED. I am not marking those reports as fixed by this PR.

A separate application report showed snd_pcm_start() itself failing during AlsaRestart(). Injected EIO can produce the same call chain, but the original report lacks the ALSA errno and device details. This patch does not establish a fix for that incident.

The original fix and reproducer commits are preserved. A follow-up commit replaces the Docker/Python harness with the standalone C reproducer. The commits may be squashed on merge.

@whatisyourname0
whatisyourname0 marked this pull request as ready for review October 17, 2026 13:38
@whatisyourname0

Copy link
Copy Markdown
Author

Hi @RossBencina, could you please approve the CI runs for this PR and take a look when you have a chance? It fixes non-MMAP capture stalling after XRUN recovery and includes a standalone reproducer. Thank you!

@whatisyourname0 whatisyourname0 changed the title fix(alsa): restart non-MMAP capture after XRUN recovery alsa: restart non-MMAP capture after XRUN recovery Oct 19, 2026
@philburk

Copy link
Copy Markdown
Collaborator

We think there is value in this PR. But we do not know how it relates to other Issues.

Also we cannot merge a PR that adds dependencies on Docker and Python.
Is it possible to create a C test program that fails without the fix and passes with the test?
Then we could have an Issue with clear repro steps.

Do "patest_underflow.c" or "patest_toomanysines.c" recreate this error?

@whatisyourname0

Copy link
Copy Markdown
Author

Hi Phil,

Thanks for reviewing this. I'll prepare a standalone C reproducer with clear steps
for a separate issue.

I checked patest_underflow.c and patest_toomanysines.c. Both open
output-only streams, so they don't exercise this capture recovery
path. The callback delay in patest_underflow.c looks like a useful
starting point for a capture test.

The failure I've reproduced is specific to non-MMAP capture:

  • snd_pcm_recover() succeeds and leaves the PCM in PREPARED, but capture itself
    never starts again. Callbacks stop while the stream remains
    active. I'll make the C test check that callbacks resume after the
    overrun, then verify close and reopen against both versions.

Issue #930 has a similar symptom, but I haven't established that it has the
same cause. #1038 fixes loops in error handling; this problem occurs
after recovery succeeds. The separate application report of
snd_pcm_start() failing also remains unconfirmed, since its ALSA
error code wasn't recorded.

I'll follow up with the C reproducer and the baseline/fixed results.

@whatisyourname0

Copy link
Copy Markdown
Author

Hi @philburk,

Thanks for the guidance. I opened #1195 with the standalone C reproducer, build steps and baseline/fixed results. The PR now uses the C test without Docker or Python dependencies.

With MOTU input through Apple VZ, the baseline stopped at five callbacks after an overrun. The fixed version recovered in all three trials and passed close/reopen checks. A separate signal check also confirmed audio after recovery. These tests use an experimental virtio-snd guest kernel; direct Linux USB audio testing is still pending.

The issue also explains the distinction from #930 and #1038. I'd appreciate your feedback when you have a chance. Thank you!

Comment thread qa/alsa-xrun/README.md Outdated
Comment on lines +78 to +79
than claiming recovery was tested. The new C mode 4 has not yet been validated
on physical input.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Do not use modes 0 through 3 as hardware proof. For physical tests, use mode 4 with an explicitly selected ALSA PCM (e.g. hw:0,0) and the normal ALSA configuration.

The new C mode 4 has not yet been validated on physical input.

Does this mean that this test and the fix have not actually been validated on real hardware?

@whatisyourname0 whatisyourname0 Oct 30, 2026 •

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Hi Ross,

Thanks for catching that. I missed updating the README after the VZ tests.

I tested the audio interface (MOTU UltraLite-mk5) connected directly by USB to a Linux machine, using the standard USB audio driver and kernel 6.8.0-136-generic. The unchanged C reproducer reproduced the stall through plughw. Repeated the comparison directly on hw, changing only the test's channel count and sample format to match the MOTU: 20 channels, packed 24-bit, 48 kHz. The PCM used RW_INTERLEAVED access.

The baseline stopped at five callbacks after the overrun and remained PREPARED. The fixed version passed three overrun/close/reopen trials, and both versions passed normal recording before and after the test. A separate sample check also confirmed nonzero audio after recovery.

These new tests use eiter no VZ nor virtio-snd. updatD the README with the exact setup and results. Thank you for pointing out the stale wording.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ALSA non-MMAP capture stops delivering callbacks after successful XRUN recovery

3 participants