From c03ba3d91efba4861f9a16a77d60c9f311dd66b6 Mon Sep 17 00:00:00 2001 From: John Hawthorn Date: Tue, 13 May 2025 22:36:09 -0700 Subject: [PATCH] Use atomics for system_working global Although it almost certainly works in this case, volatile is best not used for multi-threaded code. Using atomics instead avoids warnings from TSan. This also simplifies some logic, as system_working was previously only ever assigned to 1, so --system_working <= 0 should always return true (unless it underflowed). --- misc/tsan_suppressions.txt | 3 --- thread.c | 2 +- thread_pthread.c | 22 ++++++++++------------ thread_win32.c | 16 ++++++++-------- 4 files changed, 19 insertions(+), 24 deletions(-) diff --git a/misc/tsan_suppressions.txt b/misc/tsan_suppressions.txt index 908de1c3d93488..c788e927ddb2ec 100644 --- a/misc/tsan_suppressions.txt +++ b/misc/tsan_suppressions.txt @@ -30,9 +30,6 @@ race_top:RUBY_VM_INTERRUPTED_ANY race_top:unblock_function_set race_top:threadptr_get_interrupts -# system_working needs to be converted to atomic -race:system_working - # It's already crashing. We're doing our best signal:rb_vm_bugreport race:check_reserved_signal_ diff --git a/thread.c b/thread.c index 6089184ea9d101..fed955e87e3e9e 100644 --- a/thread.c +++ b/thread.c @@ -148,7 +148,7 @@ static int hrtime_update_expire(rb_hrtime_t *, const rb_hrtime_t); NORETURN(static void async_bug_fd(const char *mesg, int errno_arg, int fd)); MAYBE_UNUSED(static int consume_communication_pipe(int fd)); -static volatile int system_working = 1; +static rb_atomic_t system_working = 1; static rb_internal_thread_specific_key_t specific_key_count; /********************************************************************************/ diff --git a/thread_pthread.c b/thread_pthread.c index 7811d5afbf4429..4e969c41226aca 100644 --- a/thread_pthread.c +++ b/thread_pthread.c @@ -2574,7 +2574,7 @@ rb_thread_wakeup_timer_thread(int sig) timer_thread_wakeup_force(); // interrupt main thread if main thread is available - if (system_working) { + if (RUBY_ATOMIC_LOAD(system_working)) { rb_vm_t *vm = GET_VM(); rb_thread_t *main_th = vm->ractor.main_thread; @@ -3005,12 +3005,12 @@ timer_thread_func(void *ptr) RUBY_DEBUG_LOG("started%s", ""); - while (system_working) { + while (RUBY_ATOMIC_LOAD(system_working)) { timer_thread_check_signal(vm); timer_thread_check_timeout(vm); ubf_wakeup_all_threads(); - RUBY_DEBUG_LOG("system_working:%d", system_working); + RUBY_DEBUG_LOG("system_working:%d", RUBY_ATOMIC_LOAD(system_working)); timer_thread_polling(vm); } @@ -3124,18 +3124,16 @@ rb_thread_create_timer_thread(void) static int native_stop_timer_thread(void) { - int stopped; - stopped = --system_working <= 0; + RUBY_ATOMIC_SET(system_working, 0); - if (stopped) { - RUBY_DEBUG_LOG("wakeup send %d", timer_th.comm_fds[1]); - timer_thread_wakeup_force(); - RUBY_DEBUG_LOG("wakeup sent"); - pthread_join(timer_th.pthread_id, NULL); - } + RUBY_DEBUG_LOG("wakeup send %d", timer_th.comm_fds[1]); + timer_thread_wakeup_force(); + RUBY_DEBUG_LOG("wakeup sent"); + pthread_join(timer_th.pthread_id, NULL); if (TT_DEBUG) fprintf(stderr, "stop timer thread\n"); - return stopped; + + return 1; } static void diff --git a/thread_win32.c b/thread_win32.c index f9c268b117eec7..c656d79a1a5a17 100644 --- a/thread_win32.c +++ b/thread_win32.c @@ -798,14 +798,14 @@ rb_thread_create_timer_thread(void) static int native_stop_timer_thread(void) { - int stopped = --system_working <= 0; - if (stopped) { - SetEvent(timer_thread.lock); - native_thread_join(timer_thread.id); - CloseHandle(timer_thread.lock); - timer_thread.lock = 0; - } - return stopped; + RUBY_ATOMIC_SET(system_working, 0); + + SetEvent(timer_thread.lock); + native_thread_join(timer_thread.id); + CloseHandle(timer_thread.lock); + timer_thread.lock = 0; + + return 1; } static void