Every io wait took timer_th.waiting_lock to register and again to wake: one global lock serialized the fd map, the epoll_ctl calls made under it, the timer wheel and the wake-pending flags, across all fds. Split the ownership: - an fd's...ko1 (Koichi Sasada)
rb_ractor_blocking_threads_inc/dec took the VM lock on every blocking region boundary of a ractor's last runnable thread -- for a 1-thread ractor, twice per IO operation -- to maintain vm->ractor.blocking_cnt. That counter is only consum...ko1 (Koichi Sasada)
thread_sched_setup_running_threads runs on every context switch (park, resume, and both ends of every blocking region) and took the global ractor.sched.lock to maintain the running_threads and timeslice lists and running_cnt. With many ...ko1 (Koichi Sasada)
native_thread_dedicated_inc/dec run on every blocking region boundary and took ractor.sched.lock each way just to move two counters. Make snt_cnt a seq-cst atomic: leaving the pool is one FETCH_SUB, and rejoining is a CAS loop that enfo...ko1 (Koichi Sasada)
rb_gc_single_objspace_p() answered "single" as soon as ruby_single_main_ractor was set, and rb_ractor_atfork() sets it again in the child. The pre-fork Ractors' objspaces are still parked in zombie_objspaces at that point, so the child'...ko1 (Koichi Sasada)
A dedicated native thread used to sleep in two stages: native_cond_sleep() parked it on nt->cond.intr inside a blocking region, and on wakeup it went back to the scheduler to wait for its running turn on nt->cond.readyq. The thread sche...ko1 (Koichi Sasada)
The timer thread delivers expiry and fd wakeups in batches: it collects {thread, serial} pairs under timer_th.waiting_lock, releases the lock (the scheduler lock a wakeup takes must not nest inside it), and then wakes each thread. Unlin...ko1 (Koichi Sasada)
Ractor::Port#receive, Ractor.receive and Ractor.select gain a `timeout:` keyword and return nil when it passes. The wait itself stays where it is: rb_ractor_sched_wait() keeps parking the thread in the thread scheduler, so an M:N thread...ko1 (Koichi Sasada)
The test reads GC.stat and GC.stat_heap separately and asserts that the per-heap values sum to the totals. Both come from the same counters (objspace_live_slots() is the sum of the per-heap live slots, and so on), so they only disagree ...ko1 (Koichi Sasada)