From 9960da6cb895c12ea5e4f5ff5f60d95eb09dc75a Mon Sep 17 00:00:00 2001 From: Davide Beatrici Date: Tue, 14 Jan 2025 16:00:33 +0100 Subject: [PATCH] FIX(client): Infinite loop and log spam on ALSA input device disconnection A user reported that sometimes the client log gets spammed with repeated messages such as: 2025-01-10 10:24:10.638 ALSAAudioInput: No such device: No such device The error corresponds to ENODEV (19), which we treat like any other. Since it's unrecoverable, the code gets stuck in a full speed loop until the audio engine is stopped. This commit: 1. Switches from snd_pcm_prepare() to snd_pcm_recover(), which is specifically designed to recover streams. 2. Breaks the loop if the error is not one of the only three recoverable (EINTR, EPIPE, ESTRPIPE). Example log: 2025-01-14 06:16:44.494 ALSAAudioInput encountered unrecoverable error: File descriptor in bad state -> exiting... Please note that also ALSAAudioOutput doesn't explicitly handle fatal errors in its loop, but it shouldn't run "infinitely" at full speed because poll-based. (cherry picked from commit d2dfcd34a560074e576f284c867172c9a3a769d8) --- src/mumble/ALSAAudio.cpp | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/src/mumble/ALSAAudio.cpp b/src/mumble/ALSAAudio.cpp index 88a61ef003c..2a9fc6b6597 100644 --- a/src/mumble/ALSAAudio.cpp +++ b/src/mumble/ALSAAudio.cpp @@ -305,7 +305,6 @@ ALSAAudioInput::~ALSAAudioInput() { void ALSAAudioInput::run() { QMutexLocker qml(&qmALSA); - snd_pcm_sframes_t readblapp; QByteArray device_name = Global::get().s.qsALSAInput.toLatin1(); snd_pcm_hw_params_t *hw_params = nullptr; @@ -379,21 +378,27 @@ void ALSAAudioInput::run() { snd_pcm_status_dump(status, log); snd_pcm_status_free(status); #endif - readblapp = snd_pcm_readi(capture_handle, inbuff.data(), static_cast< snd_pcm_uframes_t >(wantPeriod)); - if (readblapp == -ESTRPIPE) { - qWarning("ALSAAudioInput: PCM suspended, trying to resume"); - while (bRunning && snd_pcm_resume(capture_handle) == -EAGAIN) - msleep(1000); - if ((err = snd_pcm_prepare(capture_handle)) < 0) - qWarning("ALSAAudioInput: %s: %s", snd_strerror(static_cast< int >(readblapp)), snd_strerror(err)); - } else if (readblapp == -EPIPE) { - err = snd_pcm_prepare(capture_handle); - qWarning("ALSAAudioInput: %s: %s", snd_strerror(static_cast< int >(readblapp)), snd_strerror(err)); - } else if (readblapp < 0) { - err = snd_pcm_prepare(capture_handle); - qWarning("ALSAAudioInput: %s: %s", snd_strerror(static_cast< int >(readblapp)), snd_strerror(err)); - } else if (wantPeriod == static_cast< unsigned int >(readblapp)) { - addMic(inbuff.data(), static_cast< unsigned int >(readblapp)); + const snd_pcm_sframes_t ret = snd_pcm_readi(capture_handle, inbuff.data(), wantPeriod); + if (ret >= 0) { + if (static_cast< snd_pcm_uframes_t >(ret) == wantPeriod) { + addMic(inbuff.data(), static_cast< unsigned int >(ret)); + } + } else { + err = static_cast< decltype(err) >(ret); + switch (err) { + case -EINTR: + case -EPIPE: + case -ESTRPIPE: + qWarning("ALSAAudioInput encountered unrecoverable error: %s -> exiting...", snd_strerror(err)); + while (bRunning && snd_pcm_recover(capture_handle, err, 1) == -EAGAIN) { + msleep(1000); + } + + break; + default: + qWarning("ALSAAudioInput: %s, breaking the loop...", snd_strerror(err)); + bRunning = false; + } } }