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).
This commit is contained in:
John Hawthorn 2025-05-13 22:36:09 -07:00
parent d845da05e8
commit d67d169aea
Notes: git 2025-05-15 22:18:22 +00:00
4 changed files with 19 additions and 24 deletions

View File

@ -30,9 +30,6 @@ race_top:RUBY_VM_INTERRUPTED_ANY
race_top:unblock_function_set race_top:unblock_function_set
race_top:threadptr_get_interrupts 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 # It's already crashing. We're doing our best
signal:rb_vm_bugreport signal:rb_vm_bugreport
race:check_reserved_signal_ race:check_reserved_signal_

View File

@ -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)); NORETURN(static void async_bug_fd(const char *mesg, int errno_arg, int fd));
MAYBE_UNUSED(static int consume_communication_pipe(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; static rb_internal_thread_specific_key_t specific_key_count;
/********************************************************************************/ /********************************************************************************/

View File

@ -2574,7 +2574,7 @@ rb_thread_wakeup_timer_thread(int sig)
timer_thread_wakeup_force(); timer_thread_wakeup_force();
// interrupt main thread if main thread is available // interrupt main thread if main thread is available
if (system_working) { if (RUBY_ATOMIC_LOAD(system_working)) {
rb_vm_t *vm = GET_VM(); rb_vm_t *vm = GET_VM();
rb_thread_t *main_th = vm->ractor.main_thread; rb_thread_t *main_th = vm->ractor.main_thread;
@ -3005,12 +3005,12 @@ timer_thread_func(void *ptr)
RUBY_DEBUG_LOG("started%s", ""); RUBY_DEBUG_LOG("started%s", "");
while (system_working) { while (RUBY_ATOMIC_LOAD(system_working)) {
timer_thread_check_signal(vm); timer_thread_check_signal(vm);
timer_thread_check_timeout(vm); timer_thread_check_timeout(vm);
ubf_wakeup_all_threads(); 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); timer_thread_polling(vm);
} }
@ -3124,18 +3124,16 @@ rb_thread_create_timer_thread(void)
static int static int
native_stop_timer_thread(void) native_stop_timer_thread(void)
{ {
int stopped; RUBY_ATOMIC_SET(system_working, 0);
stopped = --system_working <= 0;
if (stopped) { RUBY_DEBUG_LOG("wakeup send %d", timer_th.comm_fds[1]);
RUBY_DEBUG_LOG("wakeup send %d", timer_th.comm_fds[1]); timer_thread_wakeup_force();
timer_thread_wakeup_force(); RUBY_DEBUG_LOG("wakeup sent");
RUBY_DEBUG_LOG("wakeup sent"); pthread_join(timer_th.pthread_id, NULL);
pthread_join(timer_th.pthread_id, NULL);
}
if (TT_DEBUG) fprintf(stderr, "stop timer thread\n"); if (TT_DEBUG) fprintf(stderr, "stop timer thread\n");
return stopped;
return 1;
} }
static void static void

View File

@ -798,14 +798,14 @@ rb_thread_create_timer_thread(void)
static int static int
native_stop_timer_thread(void) native_stop_timer_thread(void)
{ {
int stopped = --system_working <= 0; RUBY_ATOMIC_SET(system_working, 0);
if (stopped) {
SetEvent(timer_thread.lock); SetEvent(timer_thread.lock);
native_thread_join(timer_thread.id); native_thread_join(timer_thread.id);
CloseHandle(timer_thread.lock); CloseHandle(timer_thread.lock);
timer_thread.lock = 0; timer_thread.lock = 0;
}
return stopped; return 1;
} }
static void static void