]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
chan_console: Fix deadlock caused by unclean thread exit.
authorNaveen Albert <asterisk@phreaknet.org>
Sat, 9 Sep 2023 15:24:37 +0000 (11:24 -0400)
committerNaveen Albert <asterisk@phreaknet.org>
Wed, 20 Sep 2023 19:15:45 +0000 (19:15 +0000)
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

index 51892123446ce96a69af203d9a301397ba2696dc..fd569b099fc99d82d934b2dfcf0894d5b361355f 100644 (file)
@@ -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);