From 0ab5dea46b59d93a7f8f7723bd208345249b3f25 Mon Sep 17 00:00:00 2001 From: Naveen Albert Date: Sat, 9 Sep 2023 11:24:37 -0400 Subject: [PATCH] chan_console: Fix deadlock caused by unclean thread exit. To terminate a console channel, stop_stream causes pthread_cancel to make stream_monitor exit. However, commit 5b8fea93d106332bc0faa4b7fa8a6ea71e546cac added locking to this function which results in deadlock due to the stream_monitor thread being killed while it's holding the pvt lock. To resolve this, a flag is now set and read to indicate abort, so the use of pthread_cancel and pthread_kill can be avoided altogether. Resolves: #308 --- channels/chan_console.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/channels/chan_console.c b/channels/chan_console.c index 5189212344..fd569b099f 100644 --- a/channels/chan_console.c +++ b/channels/chan_console.c @@ -152,6 +152,8 @@ static struct console_pvt { struct ast_frame fr; /*! Running = 1, Not running = 0 */ unsigned int streamstate:1; + /*! Abort stream processing? */ + unsigned int abort:1; /*! On-hook = 0, Off-hook = 1 */ unsigned int hookstate:1; /*! Unmuted = 0, Muted = 1 */ @@ -275,18 +277,19 @@ static void *stream_monitor(void *data) }; for (;;) { - pthread_testcancel(); console_pvt_lock(pvt); res = Pa_ReadStream(pvt->stream, buf, sizeof(buf) / sizeof(int16_t)); console_pvt_unlock(pvt); - pthread_testcancel(); - if (!pvt->owner) { + if (!pvt->owner || pvt->abort) { return NULL; } - if (res == paNoError) + if (res == paNoError) { ast_queue_frame(pvt->owner, &f); + } else { + ast_log(LOG_WARNING, "Console ReadStream failed: %s\n", Pa_GetErrorText(res)); + } } return NULL; @@ -401,8 +404,9 @@ static int stop_stream(struct console_pvt *pvt) if (!pvt->streamstate || pvt->thread == AST_PTHREADT_NULL) return 0; - pthread_cancel(pvt->thread); - pthread_kill(pvt->thread, SIGURG); + pvt->abort = 1; + /* Wait for pvt->thread to exit cleanly, to avoid killing it while it's holding a lock. */ + pthread_kill(pvt->thread, SIGURG); /* Wake it up if needed, but don't cancel it */ pthread_join(pvt->thread, NULL); console_pvt_lock(pvt);