Skip to content

[pull] master from ruby:master - #1332

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

[pull] master from ruby:master#1332
pull[bot] merged 4 commits into
turkdevops:masterfrom
ruby:master

Conversation

@pull

@pull pull Bot commented Aug 21, 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 4 commits August 21, 2026 04:13
A port's queue lives in the receiving Ractor's table keyed by the port id, so
nothing ties it to the Ractor::Port object: when the port becomes unreachable
the queue stays, and so does every message left in it.  Nobody can receive
them -- receiving needs the port -- but they are still GC roots.

A message reaching a Ractor makes that permanent.  Port#<< of a port is the
natural shape of a credit token, and a token nobody takes holds the sender's
port, which marks the sender's Ractor object, which keeps its objspace in
zombie_objspaces for the life of the process:

  200.times { port = Ractor::Port.new
              Ractor.new(port) { |p| p << Ractor::Port.new; nil }.join }
  6.times { GC.start }
  ObjectSpace.each_object(Ractor).count   # 201 before, 1 after

Mark and sweep the table itself.  ractor_port_mark notes the ids a reachable
port still carries, and rb_ractor_finish_marking drops the queues nobody
noted, which is where the ractor-local keys are already reaped for the same
reason.  Both run only where an unmarked port really is dead: after a mark
that covered every objspace -- a global GC or a full single-objspace one --
which is also when the world is stopped enough to read the table without the
owner's lock.  Hence the new argument to rb_ractor_finish_marking: a minor
mark leaves live old objects unmarked.

The single-objspace question is asked first because mmtk marks from its GC
worker threads, which cannot reach the VM to answer the other one.  Those
threads race on the flag, but they all store the same value and the reap
reads it once marking is over.

A queue starts alive so that one created between its port being marked and
the reap, which incremental marking allows, is not taken for an orphan.  It
costs the queue one more cycle before it goes.

  2000 rounds, dropping one message each, KB of RSS retained per round:

    payload           before   after
    Integer            13.0     13.3
    Ractor::Port      leaks     14.3
    shareable 10KB     89.1     62.5

The last row is not retention: the payload is collected (0 of 500 strings
survive), but the reap only runs in a global GC, so between two of them the
dropped messages hold pages.  Collecting every 20 rounds instead brings it to
1.5 KB.

The reap runs from rb_ractor_finish_marking, which only the default GC calls
-- mmtk has never called it, so the ractor-local keys it also frees are not
freed there either.  Under mmtk a dropped message still holds its port, so
the test for that is a test-all case that omits itself, next to the other
tests for per-Ractor objspace semantics.

ractor-pipeline, which sends credit tokens this way, over 60 pipelines:
455MB growing by 5.6MB each to 272MB growing by 1.5MB, with the library
unchanged.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Follow-up to 64b9d2f. The commit removed the
check completely, because the conditions were incorrect, but there are in fact
situations where the instruction is unpredictable, namely when the pre/post
index update the same register as the one to be stored, e.g. `stp x0, x1, [x0,
#0x10]!`. Corroborating the spec, our disassembler refuses to decode these
instructions.

Add condition for unpredictability as written in
https://support.arm.com/documentation/ddi0602/2026-06/Base-Instructions/STP--Store-pair-of-registers-
We can use a special value of 0 in source_hash to denote that it does not
have source_hash. This removes the has_source_hash field which saves us 8
bytes per iseq.
@pull pull Bot locked and limited conversation to collaborators Aug 21, 2026
@pull pull Bot added the ⤵️ pull label Aug 21, 2026
@pull
pull Bot merged commit ae574e7 into turkdevops:master Aug 21, 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.

4 participants