Do not count a not-yet-running thread as a Ractor barrier waiter

`rb_ractor_sched_barrier_join` unconditionally did `barrier_waiting_cnt++`.
A thread can reach it (via `vm_lock_enter`) while its Ractor's
`sched.running` is set but the thread is not yet in the VM running set
(`sched.is_running == false`) -- e.g. resuming from a blocking IO wait.
Counting such a thread pushes `barrier_waiting_cnt` past `running_cnt - 1`
and trips `VM_ASSERT(running_cnt - 1 >= barrier_waiting_cnt)` in
`ractor_sched_barrier_completed_p`; in release builds the unsigned
`(running_cnt - barrier_waiting_cnt) == 1` completion test can underflow.

Increment `barrier_waiting_cnt` only when `cr->threads.sched.is_running`
(O(1), toggled together with running_threads add/del).  A not-running
thread is parked at the barrier regardless, so it is safe to wait without
counting.

NOTE (open): this guards the count at the join site.  A stricter fix would
prevent a not-running thread from reaching barrier_join at all; that needs
care so it still waits for the barrier rather than proceeding during
compaction.  Left for review.

Reproduction is currently coupled with a separate pre-existing compaction
use-after-poison on a blocked-IO thread's STR_TMPLOCK buffer (only the
IO-read blocking path triggers this barrier window), so a standalone
passing test awaits that fix.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Koichi SasadaandClaude Opus 4.8 committed 2026-07-11 12:03:53 +09:00
1 parent f3e4fc4fee
commit 3fa1c87089
1 file changed
+10 -4
Notes: git 2026-07-11 03:04:22 +00:00
+10 -4
View File
@@ -1664,10 +1664,16 @@ rb_ractor_sched_barrier_join(rb_vm_t *vm, rb_ractor_t *cr)
ractor_sched_lock(vm, cr);
{
// running_cnt
vm->ractor.sched.barrier_waiting_cnt++;
RUBY_DEBUG_LOG("waiting_cnt:%u serial:%u", vm->ractor.sched.barrier_waiting_cnt, barrier_serial);
ractor_sched_barrier_join_signal_locked(vm);
/* A not-yet-running thread (blocking/terminating, joined only to
* sync) must not be counted, or barrier_waiting_cnt > running_cnt-1. */
if (cr->threads.sched.is_running) {
vm->ractor.sched.barrier_waiting_cnt++;
RUBY_DEBUG_LOG("waiting_cnt:%u serial:%u", vm->ractor.sched.barrier_waiting_cnt, barrier_serial);
ractor_sched_barrier_join_signal_locked(vm);
}
else {
RUBY_DEBUG_LOG("join without counting (not running) serial:%u", barrier_serial);
}
ractor_sched_barrier_join_wait_locked(vm, cr->threads.sched.running);
}
ractor_sched_unlock(vm, cr);