Skip to content

[pull] master from ruby:master - #1341

Merged
pull[bot] merged 7 commits into
turkdevops:masterfrom
ruby:master
Aug 24, 2026
Merged

[pull] master from ruby:master#1341
pull[bot] merged 7 commits into
turkdevops:masterfrom
ruby:master

Conversation

@pull

@pull pull Bot commented Aug 24, 2026

Copy link
Copy Markdown

See Commits and Changes for more details.


Created by pull[bot] (v2.0.0-alpha.4)

Can you help keep this open source service alive? 💖 Please sponsor : )

ko1 and others added 7 commits August 24, 2026 04:37
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 consumed by the win32 scheduler, so the pthread
build now skips those VM-lock sections; the per-ractor
threads.blocking_cnt and the ractor creation/exit protocol stay.

Ractor#inspect's status becomes running/terminated only ("blocking" is
gone).  The finer states were really the win32 scheduler's
in-blocking-region flag leaking into inspect (under M:N, receive/sleep/
IO waits always showed "running" anyway, and the skip above stops the
remaining flips on pthread entirely).

R ractors x 1 thread, pipe write/read round-trips each, 16-HT machine
(Ryzen 9 5900HX), RUBY_MN_THREADS=1, best of 3, mean of 2 alternating
same-tree runs:

           before     after
     1R      342k      356k
     4R      877k     1016k
     8R      884k      976k
    16R      835k      884k
    24R      793k      839k

The remaining ceiling is the scheduler-lock contention addressed by
"thread: keep context switches off the scheduler lock"; combined, 16R
reaches 3.6M rt/s.

Suggested-by: Koichi Sasada <ko1@atdot.net>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Resize IO::Buffer slices as views
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 waiter list, arming state and the flags of its io waits move
  under one of 16 fd shard locks; unrelated fds register and wake in
  parallel, and the epoll_ctl calls leave the global lock entirely
- waiting_lock keeps the wheel and fd-less timed waits
- wake_pending becomes a count under its own small lock, so an expiry
  hold and an fd event's wake can pin one thread at once
- the untimed waiter list is dropped (its only reader was a VM_ASSERT
  pass), so untimed io waits touch no global lock at all
- the fd map's chunk table becomes a fixed array with CAS-installed
  chunks, since chunks span shards

An io+timeout wait is registered in both structures; whoever clears its
flags under the fd shard owns the wakeup.  The expiry pass pops wheel
nodes under waiting_lock, pins the thread with the pending count, and
claims under the shard by (serial, fd) -- lock order is always
shard -> waiting_lock -> wake_pending_lock.

R ractor pairs (1 thread each) ping-ponging over pipes with blocking
reads, total round-trips/sec, 16-HT machine (Ryzen 9 5900HX),
RUBY_MN_THREADS=1, mean of 2 alternating same-tree runs:

               before    after
    8 pairs      63k      134k
   16 pairs      64k      143k

Messaging without fds (port ping-pong pairs) is unchanged.

A thread-heavy shape (256 threads in few ractors doing memcached round
trips) is unchanged: its wakes stay inside each ractor's readyq and the
old lock was not saturated there.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
We know the type of the proc object at allocation time, so we can allocate
exactly the amount of memory needed for that type.

We can also shrink the type from a 4 byte enum since there are only 4 types
of procs. This allows us to put some flags after it, which saves us 8 bytes.

This commit reduces symbol and proc procs by 24 bytes (80 bytes to 56 bytes).
It also reduces iseq and ifunc procs by 8 bytes (80 bytes to 72 bytes).
…il tag

- `Gem::Security::Signer#extract_name` matched `/\Aemail:/` then read
  the non-matching part of the string out of `$'`.

- Prefer `delete_prefix("email:")` as it's understandable by mortals.

- Consequently get rid of a RuboCop disable for `Performance/StartWith`.

ruby/rubygems@96978c3f4b
@pull pull Bot locked and limited conversation to collaborators Aug 24, 2026
@pull pull Bot added the ⤵️ pull label Aug 24, 2026
@pull
pull Bot merged commit b4809ba into turkdevops:master Aug 24, 2026
1 of 3 checks passed
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants