https://github.com/linux-speakup/espeakup/pull/48

commit e892e7165102c55bf8e3c71d8bf80ea485fdb440
Author: Samuel Thibault <samuel.thibault@ens-lyon.org>
Date:   Tue Mar 1 01:28:38 2022 +0100

    Throttle on EE_BUFFER_FULL errors
    
    When espeak returns EE_BUFFER_FULL we should just wait a bit before
    retrying. Also, we don't want to lose the current entry. We however want
    to wake up as soon as possible on stop request.

diff --git a/src/espeak.c b/src/espeak.c
index 135c6b5..b4b58ca 100644
--- a/src/espeak.c
+++ b/src/espeak.c
@@ -325,16 +325,16 @@ static void reinitialize_espeak(struct synth_t *s)
 	return;
 }
 
+static struct espeak_entry_t *current = NULL;
 static void queue_process_entry(struct synth_t *s)
 {
-	espeak_ERROR error;
+	espeak_ERROR error = EE_OK;
 	char markbuff[50];
-	static struct espeak_entry_t *current = NULL;
 
 	if (current != queue_peek(synth_queue)) {
 		if (current)
 			free_espeak_entry(current);
-		current = (struct espeak_entry_t *) queue_remove(synth_queue);
+		current = queue_peek(synth_queue);
 	}
 	pthread_mutex_unlock(&queue_guard);
 
@@ -386,9 +386,25 @@ static void queue_process_entry(struct synth_t *s)
 		break;
 	}
 
+	pthread_mutex_lock(&queue_guard);
 	if (error == EE_OK) {
+		/* Processed, drop it */
+		struct espeak_entry_t *unqueued = queue_remove(synth_queue);
+		assert(unqueued == current);
 		free_espeak_entry(current);
 		current = NULL;
+	} else {
+		if (error == EE_BUFFER_FULL)
+		{
+			/* Give speak a little break before retrying */
+			struct timespec timeout;
+			clock_gettime(CLOCK_REALTIME, &timeout);
+			timeout.tv_sec++;
+			/* But wake up immediately if we have to stop */
+			pthread_cond_timedwait(&wake_stop, &queue_guard, &timeout);
+		}
+		else
+			fprintf(stderr, "espeak error: %d\n", error);
 	}
 }
 
@@ -450,6 +466,7 @@ void *espeak_thread(void *arg)
 			pthread_cond_wait(&runner_awake, &queue_guard);
 
 		if (stop_requested) {
+			current = NULL;
 			stop_speech();
 			synth_queue_clear();
 			stop_requested = 0;
@@ -458,7 +475,6 @@ void *espeak_thread(void *arg)
 
 		while (should_run && queue_peek(synth_queue) && !stop_requested) {
 			queue_process_entry(s);
-			pthread_mutex_lock(&queue_guard);
 		}
 	}
 	pthread_cond_signal(&stop_acknowledged);
diff --git a/src/espeakup.c b/src/espeakup.c
index 86b3a88..baeee11 100644
--- a/src/espeakup.c
+++ b/src/espeakup.c
@@ -40,6 +40,7 @@ volatile int should_run = 1;
 espeak_AUDIO_OUTPUT audio_mode;
 
 pthread_cond_t runner_awake = PTHREAD_COND_INITIALIZER;
+pthread_cond_t wake_stop = PTHREAD_COND_INITIALIZER;
 pthread_cond_t stop_acknowledged = PTHREAD_COND_INITIALIZER;
 pthread_mutex_t queue_guard = PTHREAD_MUTEX_INITIALIZER;
 
diff --git a/src/espeakup.h b/src/espeakup.h
index 58d0f6e..62a5ca8 100644
--- a/src/espeakup.h
+++ b/src/espeakup.h
@@ -99,6 +99,7 @@ extern int self_pipe_fds[2];
 #define PIPE_WRITE_FD (self_pipe_fds[1])
 
 extern pthread_cond_t runner_awake;
+extern pthread_cond_t wake_stop;
 extern pthread_cond_t stop_acknowledged;
 extern pthread_mutex_t queue_guard;
 
diff --git a/src/softsynth.c b/src/softsynth.c
index 9eb75a4..ca2e47e 100644
--- a/src/softsynth.c
+++ b/src/softsynth.c
@@ -228,6 +228,7 @@ static void request_espeak_stop(void)
 	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)
 		// wait for acknowledgement.
 		pthread_cond_wait(&stop_acknowledged, &queue_guard);
