Fix HDMI hotplug crash by releasing mutex during blocking ALSA I/O

When HDMI is unplugged during active audio capture, the blocking
snd_pcm_readi() call was holding the mutex, preventing clean shutdown.
This caused snd_pcm_drop() to race with the blocking read, leading to
undefined behavior and crashes.

Solution mirrors PiKVM's approach:
- Release mutex before snd_pcm_readi()/snd_pcm_writei()
- Reacquire mutex after I/O completes
- Verify handle and stop flag before proceeding

This allows snd_pcm_drop() to immediately abort pending I/O when the
device is closed, ensuring clean shutdown during HDMI hotplug events.
This commit is contained in:
Alex P 2025-11-21 16:47:34 +02:00
parent 9d86b02e66
commit ac568c7bbf
2 changed files with 19 additions and 5 deletions

View File

@ -673,10 +673,16 @@ retry_read:
snd_pcm_t *handle = pcm_capture_handle; snd_pcm_t *handle = pcm_capture_handle;
// Read from hardware at hardware sample rate // Release mutex before blocking I/O to allow clean shutdown
pthread_mutex_unlock(&capture_mutex);
// Read from hardware at hardware sample rate (blocking call, no mutex held)
pcm_rc = snd_pcm_readi(handle, pcm_hw_buffer, hardware_frame_size); pcm_rc = snd_pcm_readi(handle, pcm_hw_buffer, hardware_frame_size);
if (handle != pcm_capture_handle) { // Reacquire mutex and verify device wasn't closed during read
pthread_mutex_lock(&capture_mutex);
if (handle != pcm_capture_handle || atomic_load(&capture_stop_requested)) {
pthread_mutex_unlock(&capture_mutex); pthread_mutex_unlock(&capture_mutex);
return -1; return -1;
} }
@ -884,9 +890,17 @@ retry_write:
} }
snd_pcm_t *handle = pcm_playback_handle; snd_pcm_t *handle = pcm_playback_handle;
// Release mutex before blocking I/O to allow clean shutdown
pthread_mutex_unlock(&playback_mutex);
// Write to hardware (blocking call, no mutex held)
pcm_rc = snd_pcm_writei(handle, pcm_buffer, pcm_frames); pcm_rc = snd_pcm_writei(handle, pcm_buffer, pcm_frames);
if (handle != pcm_playback_handle) { // Reacquire mutex and verify device wasn't closed during write
pthread_mutex_lock(&playback_mutex);
if (handle != pcm_playback_handle || atomic_load(&playback_stop_requested)) {
pthread_mutex_unlock(&playback_mutex); pthread_mutex_unlock(&playback_mutex);
return -1; return -1;
} }