From ae29420c11b739852927e94c0c6b8e0f0433a46d Mon Sep 17 00:00:00 2001 From: Tim Wojtulewicz Date: Thu, 2 Jul 2026 11:42:42 -0700 Subject: [PATCH] Mark thread_exit_state as voliatile to avoid optimizer ellision This avoids a hang when shutting down while running under -O2 or above. With -O2, the compiler sees that state is written at line 330 (CANCEL_LOCKED) and then unconditionally overwritten at line 334 (SELF_CANCEL) with no visible intervening read. It is free to elide the store at line 330. The memory location retains the stale value THREAD_EXIT_STATE_CANCEL_UNLOCKED (set at line 257 before the loop). The compiler does not account for the fact that pthread_setcancelstate on line 331 may cause the cleanup handler to fire asynchronously and read state through the pointer captured by pthread_cleanup_push. Co-Authored-By: Claude Opus 4.6 --- src/linux/platform.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/linux/platform.c b/src/linux/platform.c index ba198d8f..54d27296 100644 --- a/src/linux/platform.c +++ b/src/linux/platform.c @@ -116,7 +116,7 @@ linux_kqueue_interrupt(struct kqueue *kq); static void monitoring_thread_cleanup(void *arg) { - enum thread_exit_state state = *(enum thread_exit_state *)arg; + enum thread_exit_state state = *(volatile enum thread_exit_state *)arg; struct kqueue *kq, *kq_tmp; if ((state == THREAD_EXIT_STATE_CANCEL_LOCKED) || @@ -223,7 +223,7 @@ monitoring_thread_loop(UNUSED void *arg) int res = 0; pid_t my_tid; sigset_t all; - enum thread_exit_state state; + volatile enum thread_exit_state state; /* Set the thread's name to something descriptive so it shows up in gdb, * etc. glibc >= 2.1.2 supports pthread_setname_np, but this is a safer way @@ -255,7 +255,7 @@ monitoring_thread_loop(UNUSED void *arg) (void) pthread_mutex_unlock(&monitoring_thread_mtx); state = THREAD_EXIT_STATE_CANCEL_UNLOCKED; - pthread_cleanup_push(monitoring_thread_cleanup, &state) + pthread_cleanup_push(monitoring_thread_cleanup, (void*)&state) while (true) { struct epoll_event events[MONITORING_MAX_EVENTS]; bool drain_req = false;