Project

General

Profile

Actions

Bug #22524

open

Calling a shareable Proc overwrites $~ and $_ of a Thread.new/Fiber.new block

Bug #22524: Calling a shareable Proc overwrites $~ and $_ of a Thread.new/Fiber.new block

Added by Eregon (Benoit Daloze) about 21 hours ago.

Status:
Open
Assignee:
-
Target version:
-
ruby -v:
ruby 4.1.0dev (2026-11-09T15:47:20Z master 7e5d882817) +PRISM [arm64-darwin25]
[ruby-core:127385]

Description

Calling a shareable Proc which sets $~ or $_ directly from the block of Thread.new or Fiber.new overwrites the $~ and $_ of that block:

set = Ractor.shareable_proc { |s| s =~ /(.)/; $_ = s; nil }

p Thread.new { $_ = "line"; "z" =~ /(z)/; set.("a"); [$~[1], $_] }.value # => ["z", "line"]
p Fiber.new { $_ = "line"; "z" =~ /(z)/; set.("b"); [$~[1], $_] }.resume # => ["z", "line"]

def m = ($_ = "line"; "z" =~ /(z)/; SET.("c"); [$~[1], $_])
SET = set
p m # => ["z", "line"]
$ ruby -v svar.rb
ruby 4.1.0dev (2026-11-09T15:47:20Z master 7e5d882817) +PRISM [arm64-darwin25]
["a", "a"]
["b", "b"]
["z", "line"]

Ruby 4.0.7 prints ["z", "line"] three times, so this is a regression.

Cause

Since 65424e1791 (GC: per-Ractor local GC), the special variables of a shareable Proc are kept per execution context (so per Fiber) instead of in its environment. That fixed a real problem: the environment of a shareable Proc can be used by several Ractors at once, so its $~ was shared mutable state across Ractors.

But they are kept in ec->root_svar, which also holds the special variables of the root frame of the Fiber or Thread, that is, its block. So calling a shareable Proc from that block overwrites them, as above. Calls from methods or from the main script are not affected, because those frames store their special variables in their own environment.

Fix

PR: https://github.com/ruby/ruby/pull/19804

The fix uses a separate per-execution-context slot, ec->isolated_svar, for the special variables of shareable Procs. They are still per Fiber, but no longer alias the root frame. Shareable Procs still share them with each other on the same Fiber. Truly per-call special variables would require saving and restoring them around each call of a shareable Proc, which seems too invasive for this bug fix.

This also matters for https://github.com/ruby/ruby/pull/19801 (making the body of define_method shareable when possible): without this fix, a define_method method using a regexp would overwrite $~ of a Thread.new/Fiber.new block calling it directly.


Related issues 1 (1 open — 0 closed)

Related to Ruby - Bug #22216: Special variables (ex. Regexp backref and IO lastline) are thread-unsafe in some cases, incompatible with RactorOpenActions
Actions

Also available in: PDF Atom