From a3a08f9ba0d481d39604adff3c301796c429f3a0 Mon Sep 17 00:00:00 2001 From: Alexander Epaneshnikov Date: Thu, 11 Jun 2026 11:06:35 +0300 Subject: [PATCH] softsynth: bound the wait for espeak to acknowledge a stop request request_espeak_stop() waited forever for the espeak thread to acknowledge the stop. If espeak-ng is wedged inside the audio output (e.g. an ALSA device blocked or stuck returning EBUSY, as reported in issue #62), the acknowledgement never comes, and the softsynth thread stops draining /dev/softsynth forever. Speakup's kernel buffer then fills up and console output stalls, which matches the "blocks dmesg output after a couple of pages" observation in issue #45. The process goes silent and only SIGKILL gets rid of it. Wait at most 10 seconds for the acknowledgement (a normal cancellation takes milliseconds; the timeout can only trigger when espeak is truly stuck). On timeout, exit with a clear message so that the init system respawns espeakup in a clean state: our systemd unit already has Restart=always. A one-second restart beats an unkillable silent daemon, and was explicitly requested by the reporter of issue #62. Helps: https://github.com/linux-speakup/espeakup/issues/45 Helps: https://github.com/linux-speakup/espeakup/issues/62 Co-Authored-By: Claude --- src/softsynth.c | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/src/softsynth.c b/src/softsynth.c index ca2e47e..d2fe585 100644 --- a/src/softsynth.c +++ b/src/softsynth.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include "espeakup.h" @@ -223,15 +224,37 @@ static void process_buffer_acsint(struct synth_t *s, char *buf, ssize_t length) } } +/* How long to wait for the espeak thread to acknowledge a stop request + * before concluding that espeak is wedged beyond in-process recovery. */ +static const int stopAckTimeout = 10; + static void request_espeak_stop(void) { + struct timespec timeout; + int err = 0; + pthread_mutex_lock(&queue_guard); stop_requested = 1; pthread_cond_signal(&runner_awake); // Wake runner, if necessary. pthread_cond_signal(&wake_stop); // Wake runner, if necessary. - while (should_run && stop_requested) + clock_gettime(CLOCK_REALTIME, &timeout); + timeout.tv_sec += stopAckTimeout; + while (should_run && stop_requested && err != ETIMEDOUT) // wait for acknowledgement. - pthread_cond_wait(&stop_acknowledged, &queue_guard); + err = pthread_cond_timedwait(&stop_acknowledged, &queue_guard, + &timeout); + if (should_run && stop_requested) { + /* The espeak thread is stuck in a call into espeak, most likely + * on a wedged audio device. There is no way to recover from + * within the process: exit so that the init system can respawn + * us in a clean state, rather than staying silent, ignoring + * SIGTERM, and stalling the whole console by not draining + * /dev/softsynth anymore. Use _exit because exit could hang in + * library destructors while the audio device is wedged. */ + fprintf(stderr, "espeakup: espeak did not acknowledge a stop " + "request within %d seconds, aborting\n", stopAckTimeout); + _exit(3); + } pthread_mutex_unlock(&queue_guard); }