Skip to content

fix: close the ALSA PCM handle when output fails - #444

Open
spockfish wants to merge 2 commits into
devgianlu:masterfrom
verox-audio:fix/alsa-close-on-error
Open

spockfish wants to merge 2 commits into
devgianlu:masterfrom
verox-audio:fix/alsa-close-on-error

Conversation

@spockfish

Copy link
Copy Markdown

When outputLoop stops on an error, it marks the output closed without closing the PCM handle. Close() returns early for a closed output, so the handle stays open until the process exits, and every later open of the same device fails with Device or resource busy, for go-librespot itself and for any other program that wants the device.

What happens

Seen with a USB DAC that returned EIO during playback (no disconnect in the kernel log):

level=error msg="output device failed" error="ALSA error at snd_pcm_recover: Input/output error"
level=warning msg="failed load spotify:track:…" error="failed setting stream for spotify:track:…: ALSA error at snd_pcm_open: Device or resource busy"

After that, every next track and every transfer to the device failed the same way, and another player on the same machine couldn't open the DAC either, until go-librespot was restarted.

The three error branches in outputLoop (snd_pcm_recover failing, a reader error, an unsupported format) set out.closed = true and return. The player then calls out.Close(), which sees closed and skips snd_pcm_close.

Fix

  • Close the PCM handle in those branches before marking the output closed (closePcmAfterError, called with the lock held).
  • Separate commit: when snd_pcm_close fails at EOF, out.lock was unlocked twice, which panics.

Tested

On a Raspberry Pi 4 (aarch64) with a USB DAC, driving output.NewOutput directly with a silent reader:

  • snd_pcm_writei made to return -EIO once (LD_PRELOAD shim), so snd_pcm_recover fails without the device going away. Before: after Close() the PCM fd is still open and aplay on the device gets Device or resource busy. After: no PCM fd left, aplay plays.
  • USB device unbound during playback (snd_pcm_recover: No such device). Before: the fd of the removed device stays open. After: it's closed.

🤖 Generated with Claude Code

spockfish and others added 2 commits November 12, 2026 21:36
When outputLoop stops on an error (snd_pcm_recover failing, a reader
error or an unsupported format), it marked the output closed without
closing the PCM handle. Close() returns early for a closed output, so
the handle stayed open until the process exited: every later open of
the device failed with "Device or resource busy", for go-librespot and
for any other program wanting the same device.

Seen with a USB DAC returning EIO during playback: after that, every
track and every transfer failed with snd_pcm_open: Device or resource
busy until go-librespot was restarted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
If snd_pcm_close failed after EOF, outputLoop unlocked out.lock in the
error branch and again right after it, which panics with "sync: unlock
of unlocked mutex".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

1 participant