Issue #22216 has been updated by jhawthorn (John Hawthorn). I think I'm _somewhat_ sold on it being Fiber-local. There may be some incompatibilities, but the existing behaviour is already strange. Here's the first case I was worried about. If you get an Enumerator with lazy or an iterating method without a block, $1 will change in the calling method. ``` $ ruby -ve 'g = "hello".gsub(/(.)/); g.next; p $1' ruby 4.0.5 (2026-05-20 revision 64336ffd0e) +PRISM [arm64-darwin25] "h" ``` ``` $ ruby -ve 'g = "hello".gsub(/(.)/); g.next; p $1' truffleruby 33.0.1 (2026-01-20), like ruby 3.3.7, Oracle GraalVM Native [arm64-darwin23] nil ``` However Ruby 4.0 behaves a bit strange with this once we move the test code into a method ``` $ ruby -ve 'def t; g = "hello".gsub(/(.)/); g.next; p $1; end; t' ruby 4.0.5 (2026-05-20 revision 64336ffd0e) +PRISM [arm64-darwin25] nil ``` However, I think this is a bug (one I spotted elsewhere through another path and had already intended on fixing). We can change the behaviour by making the block escape before making the enumerator (the implementation issue is that the ifunc's svar_lep is stale, pointing to the stack rather than the escaped env on the heap). ``` $ ruby -ve 'def t; proc{}; g = "hello".gsub(/(.)/); g.next; p $1; end; t' ruby 4.0.5 (2026-05-20 revision 64336ffd0e) +PRISM [arm64-darwin25] "h" ``` Since this is already inconsistent, that seems like an opportunity to make the switch to Fiber local? Here's a less broken case (avoids an ifunc so doesn't have the same issue). I could see someone actually writing this and shows a compatibility issue we might face: ```ruby def report access_log = [ %{127.0.0.1 - - [04/Aug/2026:10:00:00 -0700] "GET /index.html HTTP/1.1" 200 1043}, %{10.2.3.4 - - [04/Aug/2026:10:00:01 -0700] "POST /login HTTP/1.1" 302 0}, ] e = access_log.lazy.select { |line| line =~ /"(\w+) (\S+) HTTP/ } loop do e.next puts "#{$1} #{$2}" end end report ``` ``` $ ruby -v svar_access_log3.rb ruby 4.0.5 (2026-05-20 revision 64336ffd0e) +PRISM [arm64-darwin25] GET /index.html POST /login ``` ``` $ ruby -v svar_access_log3.rb truffleruby 33.0.1 (2026-01-20), like ruby 3.3.7, Oracle GraalVM Native [arm64-darwin23] ``` ---------------------------------------- Bug #22216: Regexp backref and IO lastline are incompatible with Ractor https://bugs.ruby-lang.org/issues/22216#change-118341 * Author: headius (Charles Nutter) * Status: Open * Backport: 3.3: UNKNOWN, 3.4: UNKNOWN, 4.0: UNKNOWN ---------------------------------------- ## Problem Several Regexp-matching methods currently write (and sometimes read) the implicit "backref" `$~` variable in the local frame (and related variables like `$'`). Several IO methods read or write the "last line" `$_` variable in the same way. In both cases, the result is a mutable object, which makes these variables already problematic for ractors. Making matters worse, the frame might be shared if a proc is captured and used across threads or ractors, and there's no static way to inspect a piece of code to know if it expects to read or write these variables. Where procs can be rejected by a proc for accessing captured state, there's no such check possible for these variables. All of these facts make the backref and lastline variables fundamentally incompatible with Ractor. ## Possible remedies A wholesale removal of these variables would solve the problem, but there's a lot of code that depends on them... much of that code without even realizing it, since they might not access the variables directly. In some cases, the dependencies are internal and part of the behavior of core methods. Hard errors when using methods that read or write these variables would avoid introducing threading problems into a Ractor, but would also break a large number of commonly-used methods. There have been experiments to make these variables both frame and thread-local, but they have never been made standard. Updates to backref and lastline are visible across threads and already can lead to concurrency issues even on CRuby. Deprecating the implicit behavior and making it opt-in (or opt-out?), perhaps with keyword arguments or file pragmas, might be a halfway measure. It would probably not be an easy transition. I don't know the right path forward, but I believe this issue needs to be discussed. ## JRuby perspective We continue to mimic CRuby behavior, which has led to our users occasionally running into issues when a proc accesses these variables across threads. Our recommendation: "don't do that". We also have had our frustrations optimizing around these variables, since they implicitly require access across calls. Because we cannot statically detect when they will be used, we essentially treat all method names that might *potentially* access them as deopt triggers. It's not ideal. I'd like to hear ideas for how to make these variables less "magic", less implicit and easier to deal with across calls (and across threads/ractors). -- https://bugs.ruby-lang.org/