Skip to content

Commit 31f3647

Browse files
committed
fix(alsa): skip drain() on non-Running PCM to prevent USB kernel deadlock
After a write error, try_recover() leaves the PCM in the Prepared state. Calling drain() on a Prepared-state USB PCM can deadlock the kernel driver, causing a full system freeze that requires a physical power cycle to recover. This has been confirmed on Raspberry Pi 3 with the dwc_otg USB controller and a Focusrite Scarlett 2i4 USB audio interface. Certain tracks trigger an audio key error which causes an ALSA underrun during stop(); try_recover() succeeds but leaves the PCM in Prepared state, and the subsequent drain() call never returns. Dropping the PCM instead of draining is sufficient to release resources when the PCM is not actively running.
1 parent 33bf3a7 commit 31f3647

1 file changed

Lines changed: 9 additions & 2 deletions

File tree

playback/src/audio_backend/alsa.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::convert::Converter;
44
use crate::decoder::AudioPacket;
55
use crate::{NUM_CHANNELS, SAMPLE_RATE};
66
use alsa::device_name::HintIter;
7-
use alsa::pcm::{Access, Format, Frames, HwParams, PCM};
7+
use alsa::pcm::{Access, Format, Frames, HwParams, State, PCM};
88
use alsa::{Direction, ValueOr};
99
use std::process::exit;
1010
use thiserror::Error;
@@ -442,7 +442,14 @@ impl Sink for AlsaSink {
442442

443443
let pcm = self.pcm.take().ok_or(AlsaError::NotConnected)?;
444444

445-
pcm.drain().map_err(AlsaError::DrainFailure)?;
445+
// Only drain if the PCM is in Running state. After a write error,
446+
// try_recover() leaves the PCM in Prepared state, and calling drain()
447+
// on a Prepared-state USB PCM can deadlock the kernel driver
448+
// (confirmed on Raspberry Pi 3 with the dwc_otg USB controller).
449+
// In that case, dropping the PCM is sufficient to release resources.
450+
if pcm.state() == State::Running {
451+
pcm.drain().map_err(AlsaError::DrainFailure)?;
452+
}
446453
}
447454

448455
Ok(())

0 commit comments

Comments
 (0)