From d1f7e2384ba6c29e76f570df45301c6b6df22d44 Mon Sep 17 00:00:00 2001 From: Alexander Epaneshnikov Date: Thu, 11 Jun 2026 11:05:10 +0300 Subject: [PATCH] espeak: do not call espeak_Cancel() while holding queue_guard When a flush is requested, the espeak thread called stop_speech() -> espeak_Cancel() with queue_guard held. espeak_Cancel() waits for espeak-ng's internal say thread to acknowledge the cancellation, and that thread can be blocked indefinitely inside a blocking ALSA call (snd_pcm_writei/snd_pcm_drain on a wedged device, as seen with EBUSY errors). In that case queue_guard was held forever, which in turn: - blocked the signal thread on pthread_mutex_lock(), so SIGINT/SIGTERM appeared to be ignored and only SIGKILL could end the process; - left the softsynth thread stuck in request_espeak_stop(), so /dev/softsynth was no longer drained, speakup's kernel buffer filled up, and console output (e.g. dmesg) stalled. Release queue_guard around the espeak_Cancel() call. This is safe because the only queue producer, the softsynth thread, is blocked waiting for stop_acknowledged for as long as stop_requested is set, so the queue cannot be mutated concurrently. Helps: https://github.com/linux-speakup/espeakup/issues/45 Helps: https://github.com/linux-speakup/espeakup/issues/62 Co-Authored-By: Claude --- src/espeak.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/espeak.c b/src/espeak.c index 90bdfc6..41a7e99 100644 --- a/src/espeak.c +++ b/src/espeak.c @@ -492,7 +492,16 @@ void *espeak_thread(void *arg) if (stop_requested) { current = NULL; + /* Call into espeak with queue_guard released: espeak_Cancel + * can take time, or even block indefinitely when the audio + * output is wedged, and holding the lock here would prevent + * the other threads from ever making progress again. The + * queue cannot change concurrently: the only producer (the + * softsynth thread) is blocked waiting for stop_acknowledged + * as long as stop_requested is set. */ + pthread_mutex_unlock(&queue_guard); stop_speech(); + pthread_mutex_lock(&queue_guard); synth_queue_clear(); stop_requested = 0; pthread_cond_signal(&stop_acknowledged);