ml.ruby-lang.org
Sign In Sign Up
Manage this list Sign In Sign Up

Keyboard Shortcuts

Thread View

  • j: Next unread message
  • k: Previous unread message
  • j a: Jump to all threads
  • j l: Jump to MailingList overview

ruby-core

Thread Start a new thread
Download
Threads by month
  • ----- 2026 -----
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2025 -----
  • December
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2024 -----
  • December
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2023 -----
  • December
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2022 -----
  • December
  • November
ruby-core@ml.ruby-lang.org

September 2026

  • 2 participants
  • 117 discussions
[ruby-core:115328] [Ruby master Bug#19996] `RUBY_MN_THREADS=1` triggers Action Cable unit test failures
by yahonda (Yasuo Honda) 08 Sep '26

08 Sep '26
Issue #19996 has been reported by yahonda (Yasuo Honda). ---------------------------------------- Bug #19996: `RUBY_MN_THREADS=1` triggers Action Cable unit test failures https://bugs.ruby-lang.org/issues/19996 * Author: yahonda (Yasuo Honda) * Status: Open * Priority: Normal * ruby -v: ruby 3.3.0dev (2023-11-09T16:24:15Z :detached: c49adfab5d) +MN [x86_64-linux] * Backport: 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN ---------------------------------------- ### Steps to reproduce 1. Install `ruby 3.3.0dev` 2. Set `RUBY_MN_THREADS=1` environment variable 3. git clone https://github.com/rails/rails cd rails rm Gemfile.lock bundle install cd actioncable bin/test test/channel/base_test.rb test/subscription_adapter/redis_test.rb test/channel/test_case_test.rb test/subscription_adapter/redis_test.rb test/client_test.rb --seed 14800 ``` ### Expected behavior It should pass as not setting ``` $ unset RUBY_MN_THREADS $ bin/test test/channel/base_test.rb test/subscription_adapter/redis_test.rb test/channel/test_case_test.rb test/subscription_adapter/redis_test.rb test/client_test.rb --seed 14800 /home/yahonda/.rbenv/versions/trunk/lib/ruby/gems/3.3.0+0/gems/minitest-5.20.0/lib/minitest.rb:3: warning: mutex_m which will no longer be part of the default gems since Ruby 3.4.0. Add mutex_m to your Gemfile or gemspec. Run options: --seed 14800 # Running: ......................................................................... Finished in 12.031310s, 6.0675 runs/s, 46.7115 assertions/s. 73 runs, 562 assertions, 0 failures, 0 errors, 0 skips $ ``` ### Actual behavior It usually fails as follows. ``` $ bin/test test/channel/base_test.rb test/subscription_adapter/redis_test.rb test/channel/test_case_test.rb test/subscription_adapter/redis_test.rb test/client_test.rb --seed 14800 /home/yahonda/.rbenv/versions/trunk/lib/ruby/gems/3.3.0+0/gems/minitest-5.20.0/lib/minitest.rb:3: warning: mutex_m which will no longer be part of the default gems since Ruby 3.4.0. Add mutex_m to your Gemfile or gemspec. Run options: --seed 14800 # Running: ..................................................F Failure: RedisAdapterTest::AlternateConfiguration#test_channel_prefix [/home/yahonda/src/github.com/rails/rails/actioncable/test/subscription_adapter/common.rb:35]: Expected #<Concurrent::Event:0x00007f0b2698d4f0 @__Lock__=#<Thread::Mutex:0x00007f0b26f8cab8>, @__Condition__=#<Thread::ConditionVariable:0x00007f0b26f8ca90>, @set=false, @iteration=0> to be set?. bin/test test/subscription_adapter/channel_prefix.rb:6 .F Failure: RedisAdapterTest::AlternateConfiguration#test_multiple_broadcast [/home/yahonda/src/github.com/rails/rails/actioncable/test/subscription_adapter/common.rb:35]: Expected #<Concurrent::Event:0x00007f0b2698a4d0 @__Lock__=#<Thread::Mutex:0x00007f0b26fac688>, @__Condition__=#<Thread::ConditionVariable:0x00007f0b26fac4f8>, @set=false, @iteration=0> to be set?. bin/test test/subscription_adapter/common.rb:74 E Error: ClientTest#test_interacting_clients: ThreadError: queue empty <internal:thread_sync>:18:in `pop' /home/yahonda/src/github.com/rails/rails/actioncable/test/client_test.rb:168:in `read_message' /home/yahonda/src/github.com/rails/rails/actioncable/test/client_test.rb:241:in `block (2 levels) in test_interacting_clients' /home/yahonda/src/github.com/rails/rails/actioncable/test/client_test.rb:218:in `block (2 levels) in concurrently' /home/yahonda/.rbenv/versions/trunk/lib/ruby/gems/3.3.0+0/gems/concurrent-ruby-1.2.2/lib/concurrent-ruby/concurrent/promises.rb:1583:in `evaluate_to' /home/yahonda/.rbenv/versions/trunk/lib/ruby/gems/3.3.0+0/gems/concurrent-ruby-1.2.2/lib/concurrent-ruby/concurrent/promises.rb:1766:in `block in on_resolvable' /home/yahonda/.rbenv/versions/trunk/lib/ruby/gems/3.3.0+0/gems/concurrent-ruby-1.2.2/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb:352:in `run_task' /home/yahonda/.rbenv/versions/trunk/lib/ruby/gems/3.3.0+0/gems/concurrent-ruby-1.2.2/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb:343:in `block (3 levels) in create_worker' <internal:kernel>:187:in `loop' /home/yahonda/.rbenv/versions/trunk/lib/ruby/gems/3.3.0+0/gems/concurrent-ruby-1.2.2/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb:334:in `block (2 levels) in create_worker' /home/yahonda/.rbenv/versions/trunk/lib/ruby/gems/3.3.0+0/gems/concurrent-ruby-1.2.2/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb:333:in `catch' /home/yahonda/.rbenv/versions/trunk/lib/ruby/gems/3.3.0+0/gems/concurrent-ruby-1.2.2/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb:333:in `block in create_worker' /home/yahonda/.rbenv/versions/trunk/lib/ruby/gems/3.3.0+0/gems/concurrent-ruby-1.2.2/lib/concurrent-ruby/concurrent/promises.rb:1258:in `raise' /home/yahonda/.rbenv/versions/trunk/lib/ruby/gems/3.3.0+0/gems/concurrent-ruby-1.2.2/lib/concurrent-ruby/concurrent/promises.rb:1258:in `wait_until_resolved!' /home/yahonda/.rbenv/versions/trunk/lib/ruby/gems/3.3.0+0/gems/concurrent-ruby-1.2.2/lib/concurrent-ruby/concurrent/promises.rb:988:in `value!' /home/yahonda/src/github.com/rails/rails/actioncable/test/client_test.rb:218:in `map' /home/yahonda/src/github.com/rails/rails/actioncable/test/client_test.rb:218:in `concurrently' /home/yahonda/src/github.com/rails/rails/actioncable/test/client_test.rb:240:in `block in test_interacting_clients' /home/yahonda/src/github.com/rails/rails/actioncable/test/client_test.rb:90:in `with_puma_server' /home/yahonda/src/github.com/rails/rails/actioncable/test/client_test.rb:234:in `test_interacting_clients' bin/test test/client_test.rb:233 E Error: ClientTest#test_disappearing_client: ThreadError: queue empty <internal:thread_sync>:18:in `pop' /home/yahonda/src/github.com/rails/rails/actioncable/test/client_test.rb:168:in `read_message' /home/yahonda/src/github.com/rails/rails/actioncable/test/client_test.rb:275:in `block in test_disappearing_client' /home/yahonda/src/github.com/rails/rails/actioncable/test/client_test.rb:90:in `with_puma_server' /home/yahonda/src/github.com/rails/rails/actioncable/test/client_test.rb:273:in `test_disappearing_client' bin/test test/client_test.rb:272 .................. Finished in 1323.812615s, 0.0551 runs/s, 0.3830 assertions/s. 73 runs, 507 assertions, 2 failures, 2 errors, 0 skips $ ``` -- https://bugs.ruby-lang.org/
3 2
0 0
[ruby-core:126594] [Ruby Feature#17513] Methods of shareable objects and UnboundMethods should be shareable
by ko1 (Koichi Sasada) 08 Sep '26

08 Sep '26
Issue #17513 has been updated by ko1 (Koichi Sasada). Status changed from Assigned to Closed 4c893e2ff1 ---------------------------------------- Feature #17513: Methods of shareable objects and UnboundMethods should be shareable https://bugs.ruby-lang.org/issues/17513#change-118819 * Author: marcandre (Marc-Andre Lafortune) * Status: Closed * Assignee: ko1 (Koichi Sasada) ---------------------------------------- ```ruby class Foo def foo end end f = Foo.new.freeze Ractor.shareable?(f) # => true Ractor.make_shareable(f.method(:foo).to_proc) # => Proc, ok Ractor.make_shareable(f.method(:foo)) # => Ractor::Error, expected Method Ractor.make_shareable(Foo.instance_method(:foo)) # => Ractor::Error, expected UnboundMethod ``` -- https://bugs.ruby-lang.org/
1 0
0 0
[ruby-core:118182] [Ruby master Bug#20526] File.open(encoding: "bom|utf-8") converts "\r\n" to "\n" on Windows
by kou (Kouhei Sutou) 08 Sep '26

08 Sep '26
Issue #20526 has been reported by kou (Kouhei Sutou). ---------------------------------------- Bug #20526: File.open(encoding: "bom|utf-8") converts "\r\n" to "\n" on Windows https://bugs.ruby-lang.org/issues/20526 * Author: kou (Kouhei Sutou) * Status: Open * Target version: 3.2 * ruby -v: ruby 3.2.2 (2023-03-30 revision e51014f9c0) [x64-mingw-ucrt] * Backport: 3.1: UNKNOWN, 3.2: UNKNOWN, 3.3: UNKNOWN ---------------------------------------- I'm not sure whether this is an intentional behavior but it seems that `encoding: "utf-8"` doesn't change newline conversion but `encoding: "bom|utf-8"` changes newline conversion: ```ruby File.write("a.txt", "a\r\n") File.read("a.txt").bytes # => [97, 13, 10] File.open("a.txt", encoding: "utf-8") {|f| f.read.bytes} # => [97, 10, 10] File.open("a.txt", encoding: "bom|utf-8") {|f| f.read.bytes} # => [97, 10] XXX: \r\n -> \n File.open("a.txt", encoding: "bom|utf-8", universal_newline: false) {|f| f.read.bytes} # => [97, 13, 10] ``` Note that the `XXX: ` line the above codes. Is this an intentional behavior? -- https://bugs.ruby-lang.org/
3 5
0 0
[ruby-core:125706] [Ruby Bug#22106] Getting a segfault when using finalizers and/or WeakMap in Ractors
by miles-georgi (Miles Georgi) 08 Sep '26

08 Sep '26
Issue #22106 has been reported by miles-georgi (Miles Georgi). ---------------------------------------- Bug #22106: Getting a segfault when using finalizers and/or WeakMap in Ractors https://bugs.ruby-lang.org/issues/22106 * Author: miles-georgi (Miles Georgi) * Status: Open * ruby -v: ruby 4.0.5 (2026-05-20 revision 64336ffd0e) +PRISM [x86_64-linux] * Backport: 3.3: UNKNOWN, 3.4: UNKNOWN, 4.0: UNKNOWN ---------------------------------------- If I run the following script I get a segfault: ```ruby def ractor_and_port_counts total_ractor_count = total_ractor_port_count = open_ractor_count = open_ractor_port_count = 0 ObjectSpace.each_object do |object| case object when Ractor total_ractor_count += 1 open_ractor_count += 1 unless object.default_port.closed? when Ractor::Port total_ractor_port_count += 1 open_ractor_port_count += 1 unless object.closed? end end [total_ractor_count, open_ractor_count, total_ractor_port_count, open_ractor_port_count] end def show_ractor_counts GC.start pp ractor_and_port_counts end class Wrapper class << self def finalizer_proc proc do |id| wrapper_to_ractor = Ractor[:wrapper_to_ractor] if wrapper_to_ractor wrapper_to_ractor[id]&.each_key { |ractor| ractor << :close } rescue Ractor::ClosedError end end end def setup_finalizer(wrapper) wrapper_to_ractor = Ractor[:wrapper_to_ractor] ||= {} map = wrapper_to_ractor[wrapper.object_id] ||= ObjectSpace::WeakMap.new map[wrapper.ractor] = true ObjectSpace.define_finalizer(wrapper, &finalizer_proc) end end attr_reader :ractor def initialize(object) @ractor = Ractor.new do loop { break if receive == :close } o = nil end @ractor.send(object, move: true) self.class.setup_finalizer(self) end end class Outer def initialize = @inner = Wrapper.new(Inner.new) end class Inner; end show_ractor_counts outer = Wrapper.new(Outer.new) show_ractor_counts outer = nil # Removing the following line makes it so it only segfaults sometimes instead of everytime show_ractor_counts ``` I'll attach the script and the whole segfault but part of the segfault is: ``` <internal:gc>:44: [BUG] FL_FINALIZE flag is set, but finalizers are not found ruby 4.0.5 (2026-05-20 revision 64336ffd0e) +PRISM [x86_64-linux] -- Control frame information ----------------------------------------------- c:0004 p:0010 s:0023 e:000018 l:y b:0001 METHOD <internal:gc>:44 c:0003 p:0004 s:0011 e:000010 l:y b:0001 METHOD test_scripts/finalizer-in-ractor-test-segfault:23 c:0002 p:0075 s:0007 E:000f98 l:n b:---- EVAL test_scripts/finalizer-in-ractor-test-segfault:72 [FINISH] c:0001 p:0000 s:0003 E:000b40 l:y b:---- DUMMY [FINISH] -- Ruby level backtrace information ---------------------------------------- test_scripts/finalizer-in-ractor-test-segfault:72:in '<main>' test_scripts/finalizer-in-ractor-test-segfault:23:in 'show_ractor_counts' <internal:gc>:44:in 'start' ``` This happens in both a local build of 4.1-dev and 4.0.5 I suspect one might need to put GC.start at the end of the script if unable to reproduce. ---Files-------------------------------- segfault.txt (22 KB) finalizer-in-ractor-test-segfault (1.66 KB) -- https://bugs.ruby-lang.org/
2 2
0 0
[ruby-core:126572] [Ruby Bug#22293] `String#unicode_normalize` does not work in non-main Ractor
by nobu (Nobuyoshi Nakada) 08 Sep '26

08 Sep '26
Issue #22293 has been reported by nobu (Nobuyoshi Nakada). ---------------------------------------- Bug #22293: `String#unicode_normalize` does not work in non-main Ractor https://bugs.ruby-lang.org/issues/22293 * Author: nobu (Nobuyoshi Nakada) * Status: Open * Assignee: ko1 (Koichi Sasada) * Backport: 3.3: UNKNOWN, 3.4: UNKNOWN, 4.0: UNKNOWN ---------------------------------------- This fails consistently since Ruby 3.0 (except for using `#take` instead of `#value`). ```console $ ruby -v -W:no-ex -e 'p Ractor.new{"aaa".unicode_normalize}.value' ruby 4.1.0dev (2026-09-04T13:17:34Z master 3643154e6d) +PRISM [arm64-darwin25] #<Thread:0x0000000123bf0008 run> terminated with exception (report_on_exception is true): /opt/local/lib/ruby/4.1.0+4/unicode_normalize/normalize.rb:138:in 'UnicodeNormalize.normalize': can not access non-shareable objects in constant UnicodeNormalize::NF_HASH_C by non-main ractor. (Ractor::IsolationError) from -e:1:in 'String#unicode_normalize' from -e:1:in 'block in <main>' -e:1:in 'Ractor#value': thrown by remote Ractor. (Ractor::RemoteError) from -e:1:in '<main>' /opt/local/lib/ruby/4.1.0+4/unicode_normalize/normalize.rb:138:in 'UnicodeNormalize.normalize': can not access non-shareable objects in constant UnicodeNormalize::NF_HASH_C by non-main ractor. (Ractor::IsolationError) from -e:1:in 'String#unicode_normalize' from -e:1:in 'block in <main>' ``` -- https://bugs.ruby-lang.org/
2 1
0 0
[ruby-core:126538] [Ruby Feature#22277] Make `rb_gc_register_address` Ractor-local
by eightbitraptor (Matt V-H) 07 Sep '26

07 Sep '26
Issue #22277 has been reported by eightbitraptor (Matt V-H). ---------------------------------------- Feature #22277: Make `rb_gc_register_address` Ractor-local https://bugs.ruby-lang.org/issues/22277 * Author: eightbitraptor (Matt V-H) * Status: Open * Assignee: eightbitraptor (Matt V-H) * Target version: 4.1 ---------------------------------------- ## Summary [Github PR #18393](https://github.com/ruby/ruby/pull/18393) `rb_gc_register_address` stores its entries in a single VM-wide list. This means that every Ractor local GC walks the list under a VM wide lock, which causes contention between Ractors attempting to GC. The associated PR changes the storage to per-ractor lists, removing the lock. However, due to potential Ractor isolation issues, we've restricted `rb_gc_register_address` and `rb_gc_unregister_address` to the main ractor. We would like to have a discussion about this restriction before going ahead with this change. ## Background Each ractor owns an objspace. A local GC marks only that ractor's roots and sweeps only its own pages. Using a VM-wide list with this design means that: - Every ractor's local GC scans every slot, under a shared lock. - A slot can name an object in another ractor's objspace. - If ractor A registers a slot and the slot's value is an unshareable object owned by ractor B, every ractor's GC marks the object. Storing it there violates the Ractor isolation guarantees, but the object stays alive, so it works. Changing this so that each Ractor maintains it's own list changes this so that only the registering Ractor's GC scans it's own list, which means we don't need to do this under a lock. However this introduces a situation where a slot on Ractor A's list holds an unshareable object owned by Ractor B, then Ractor B's local GC will not scan A's list, and A's local GC won't mark objects in B's objspace (because mark_maybe filters by objspace). This means that B can collect the object while A holds a reference to it, leading to a potential use-after-free. Because `rb_gc_register_address` and `rb_gc_unregister_address` are part of the public C API. It's tough to enforce a contract that restricts a slots value to one that's either shareable or owned by the main Ractor, because we register addresses, and any C extension can manipulate the value at that address at any time. ## Implementation detail The associated PR adds a "registered_addrs" array and a listed flag to each `rb_ractor_t` instance. When an address registered the Ractor in question is added to a VM local registry of Ractors who's lists are not empty, so that any calls to `rb_gc_unregister_address` can find the registering Ractor easily. Each Ractor local GC marks the current ractors list via `rb_ractor_mark_local_roots` and each Global GC marks every Ractors list, including terminated Ractors whose structs haven't been freed yet. The `rb_gc_register_address` and `rb_gc_unregister_address` functions now raise `Ractor::UnsafeError` when run from a non-main Ractor. This is a fairly blunt mechanism for ensuring that we can't end up with dangling pointers, but does restrict the usage of this API in ways that might be problematic. ## Concerns The main concern with this approach is the main Ractor only restriction? This is a semantic change from how the existing API works, and does restrict the functionality somewhat. This will only affect gems with C extensions that register and unregister objects dynamically at runtime. Registration during `Init_` functions still runs on the main Ractor and is unaffected. On 2026-08-25 we ran a codesearch over an index of published gems, looking for gems that both call `rb_ext_ractor_safe(true)` to declare themselves ractor safe and also call `rb_gc_register_address`. We found a total of 14 packages that matched both calls. Of which 6 were forks of other packages also in the list. Of the remaining 8 packages, 5 contained `rb_gc_register_address` calls that were _only_ within `Init_` functions. These would be unaffected by the change. The remaining 3 are [`oj`](https://rubygems.org/gems/oj), [`ox`](https://rubygems.org/gems/ox), and [`stack_trace`](https://rubygems.org/gems/stack_trace). Given the infrequency of this usage in real world Ruby applications. Should we add some documentation around `rb_gc_register_address` to document it's restricted usage, and patch the affected gems upstream? -- https://bugs.ruby-lang.org/
3 3
0 0
[ruby-core:126235] [Ruby Feature#22226] Ractor: class/module ownership -- restrict modification to the Ractor that created it
by ko1 (Koichi Sasada) 07 Sep '26

07 Sep '26
Issue #22226 has been reported by ko1 (Koichi Sasada). ---------------------------------------- Feature #22226: Ractor: class/module ownership -- restrict modification to the Ractor that created it https://bugs.ruby-lang.org/issues/22226 * Author: ko1 (Koichi Sasada) * Status: Open * Assignee: ko1 (Koichi Sasada) * Target version: 4.1 ---------------------------------------- ## Abstract Every class/module records the Ractor that created it as its *owner*. Only the owner Ractor can modify it. Reading is unchanged and allowed from any Ractor. This replaces several ad-hoc "main Ractor only" rules with a single rule keyed on the creator. It **relaxes** the rules for classes a Ractor creates itself, and **tightens** them for classes created by somebody else. No Ruby-level API is added: ownership is recorded internally and is not exposed. --- ## 1. Motivation ### 1.1 The current rules ask the wrong question Today's restrictions ask *"are you the main Ractor?"*. They never ask *"did you create this class?"*. The result is inconsistent in both directions. A non-main Ractor may freely redefine **any** class in the process: ```ruby class C; end Ractor.new { C.define_method(:m) { 1 }; :done }.value #=> :done (works today) Ractor.new { class String; def foo = 1; end; :done }.value #=> :done (works today) Ractor.new { def f = 42; f() }.value #=> 42 (works today; defines Object#f) Ractor.new { Object.send(:remove_const, :FOO); :done }.value #=> :done (works today) ``` ...but it may **not** set an instance variable on a class it created itself: ```ruby Ractor.new { k = Class.new # nobody else can even name this class yet k.instance_variable_set(:@iv, 1) }.value #=> Ractor::IsolationError: # can not set instance variables of classes/modules by non-main Ractors ``` ...nor give it an unshareable constant: ```ruby Ractor.new { k = Class.new k.const_set(:X, "str".dup) }.value #=> Ractor::IsolationError: # can not set constants with non-shareable objects by non-main Ractors ``` The first group is unrestricted although it affects the whole process. The second group is forbidden although it affects nothing but the Ractor doing it. The rules are exactly inverted with respect to who is actually affected. ### 1.2 A Ractor's classes can be redefined under it by another Ractor Because any Ractor may modify any class, the code a Ractor runs can be changed by somebody else at any moment: ```ruby Ractor.new { class String; def upcase = :patched; end; :done }.value "abc".upcase #=> :patched # evaluated in the *main* Ractor ``` ```ruby FOO = 1 Ractor.new { Object.send(:remove_const, :FOO); :removed }.value FOO #=> NameError: uninitialized constant FOO ``` Both of these run fine today. There is no way for a Ractor to rely on the classes it uses. ### 1.3 Class internals have no single writer `m_tbl`, `const_tbl` and the class fields can be written by any Ractor today. This means the implementation cannot assume a single writer for any class, which stands in the way of synchronization and caching work (and it is also how the `cvar_overtaken()` bug below arose: a *read* path physically deletes a table entry, potentially of another Ractor's class). --- ## 2. Proposal A class/module records its creator Ractor as its **owner**. Only the owner may: * define/remove/undef methods, `alias`, change method visibility * `include`/`prepend` into it, and `Module#refine` targeting it * define/remove constants, register `autoload` * set instance variables on the class/module object Non-owner Ractors get `Ractor::IsolationError`. **Reading is completely unchanged**: method calls, instantiation, subclassing, constant reads and instance-variable reads all work from any Ractor as before. In exchange, the previous "main Ractor only" rules for class instance variables and unshareable constant values become "**owner Ractor only**". A Ractor gets full use of the classes it creates. Since all classes/modules created at boot or by main-Ractor code (including everything loaded by `require`) are owned by the main Ractor, this is equivalent to today's rules for programs that do not create classes inside non-main Ractors. --- ## 3. Examples ### 3.1 Newly allowed: a Ractor can fully use the classes it creates Instance variables on a class the Ractor created: ```ruby Ractor.new { k = Class.new k.instance_variable_set(:@iv, 1) # was: IsolationError, even for a shareable value! k.instance_variable_set(:@iv, "str".dup) # was: IsolationError k.instance_variable_get(:@iv) #=> "str" }.value ``` **Unshareable values in constants** of a class the Ractor created. This was the main Ractor's privilege before; now every Ractor has it for its own classes: ```ruby Ractor.new { k = Class.new k::BUF = "buffer".dup # unshareable value; was: IsolationError k::BUF << "!" # ...and the owner can freely use it k::BUF #=> "buffer!" }.value Ractor.new { k = Class.new k.const_set(:LIST, [1, 2, 3]) # an unfrozen Array is unshareable too k::LIST << 4 k::LIST #=> [1, 2, 3, 4] }.value ``` The value stays private to the owner -- reading it from another Ractor raises, which is exactly the rule that applied to the main Ractor before: ```ruby port = Ractor::Port.new r = Ractor.new(port) { |pt| k = Class.new; k::BUF = "buffer".dup; pt << k; Ractor.receive } k = port.receive k::BUF #=> Ractor::IsolationError: can not access non-shareable objects in constant # #<Class:0x...>::BUF of a class/module created by another Ractor. ``` A *shareable* value in the same position is readable from anywhere, as before: ```ruby r = Ractor.new(port) { |pt| k = Class.new; k::N = 42; pt << k; Ractor.receive } k = port.receive k::N #=> 42 ``` Classes defined under a module the Ractor created itself work fully: ```ruby Ractor.new { mod = Module.new mod.const_set(:Foo, Class.new { def m = :ok }) mod::Foo.new.m #=> :ok }.value ``` ### 3.2 Newly prohibited: modifying a class created by another Ractor ```ruby class C; end module M; end Ractor.new { def f = 42 }.value #=> can not modify Object because it is created by another Ractor # (top-level `def` defines a private method on Object) Ractor.new { class String; def foo = 1; end }.value #=> can not modify String because it is created by another Ractor Ractor.new { C.define_method(:m) { 1 } }.value Ractor.new { C.send(:alias_method, :a, :inspect) }.value Ractor.new { C.send(:private, :inspect) }.value Ractor.new { C.send(:undef_method, :inspect) }.value Ractor.new { C.include(M) }.value Ractor.new { C.prepend(M) }.value Ractor.new { Module.new { refine(C) { def z = 1 } } }.value Ractor.new { C.autoload(:Zz, "zz") }.value #=> can not modify C because it is created by another Ractor Ractor.new { C.const_set(:X, 1) }.value # note: even a *shareable* value now raises #=> can not set constants of classes/modules created by another Ractor Ractor.new { class TopCls; end }.value # a top-level class name... Ractor.new { module TopMod; end }.value # ...and a top-level module name #=> can not set constants of classes/modules created by another Ractor # (both write a constant into Object) Ractor.new { Object.send(:remove_const, :FOO) }.value #=> can not modify Object because it is created by another Ractor Ractor.new { C.instance_variable_set(:@iv, 1) }.value #=> can not set instance variables of classes/modules created by another Ractor ``` Patterns that stop working: ```ruby # a registry in Class#inherited that writes into the superclass Base = Class.new { def self.inherited(sub) = const_set(:"Sub#{sub.object_id}", sub) } Ractor.new { Class.new(Base) }.value #=> can not set constants of classes/modules created by another Ractor # lazy definition on first touch M2 = Module.new { def self.const_missing(n) = const_set(n, Class.new) } Ractor.new { M2::Foo }.value #=> can not set constants of classes/modules created by another Ractor ``` Both work if the definition happens before the Ractor is spawned (eager loading). ### 3.3 Unchanged ```ruby Ractor.new { "abc".upcase }.value #=> "ABC" Ractor.new { C.new }.value # instantiation Ractor.new { Class.new(String) { def m = :ok }.new.m }.value #=> :ok (subclassing is creation) Ractor.new { require "time"; Time.now.respond_to?(:xmlschema) }.value #=> true ``` `require` from a non-main Ractor keeps working because `Ractor#require` performs the load on the main Ractor, so the library's classes are defined by (and owned by) the main Ractor. `def` on an object a Ractor created is also unaffected -- an ordinary object's singleton class is created by, and owned by, the Ractor that triggers it: ```ruby Ractor.new { o = Object.new; def o.f = :ok; o.f }.value #=> :ok ``` ### 3.4 Summary table | operation from a non-main Ractor | today | proposal | |---|---|---| | define a method on a foreign class | OK | **IsolationError** | | top-level `def` | OK | **IsolationError** | | `include`/`prepend`/`refine` a foreign class | OK | **IsolationError** | | set a **shareable** constant on a foreign class | OK | **IsolationError** | | set an **unshareable** constant on a foreign class | IsolationError | IsolationError | | `remove_const` / `autoload` on a foreign class | OK | **IsolationError** | | set an ivar on a foreign class | IsolationError | IsolationError | | define a method on its **own** class | OK | OK | | set a **shareable** constant on its **own** class | OK | OK | | set an **unshareable** constant on its **own** class | IsolationError | **OK** | | set an ivar on its **own** class | IsolationError | **OK** | | read / call / instantiate / subclass any class | OK | OK | --- ## 4. How the proposal answers the motivation * **1.1 (rules ask the wrong question)** -- the rules now ask "did you create this?" instead of "are you main?". Every restriction is on the class of somebody else, and every relaxation is on your own class. The two inverted cases in 1.1 both flip to the right side. The old "main Ractor only" rules become the special case *"boot-time classes are owned by the main Ractor"*, so they are no longer separate rules. * **1.2 (classes changed under you)** -- no Ractor can modify a class it did not create, so the classes a Ractor uses cannot be redefined by another Ractor. * **1.3 (no single writer)** -- every class/module now has exactly one Ractor which can write its method table, constant table and fields. --- ## 5. What this does **not** give Ownership bounds *who may write*, not *what readers may see*. Reads stay unsynchronized and a class modification is not atomic, so a non-owner Ractor can still observe a class in the middle of being modified by its owner: after the first `def` of a `class ... end` body but before the second, or while `include`/`prepend` is rewiring the ancestor chain. The guarantee is **a single writer per class/module**, not a consistent view for readers. This proposal is not, and does not claim to be, full isolation of class state. --- ## 6. Details * **Singleton classes / metaclasses** are owned by the owner of the object they are attached to, not by the Ractor that happened to trigger their lazy creation. `def C.foo` is allowed exactly for the owner of `C`: ```ruby class C; end Ractor.new { C.singleton_class; :touched }.value # another Ractor touches it first class << C; def foo = :ok; end # still fine in main C.foo #=> :ok Ractor.new { class << C; def bar = 1; end }.value #=> IsolationError ``` * **Terminated owner**: a class whose owner Ractor has finished becomes permanently read-only for everybody, including the main Ractor. ```ruby K = Ractor.new { Class.new { def x = :made_in_ractor } }.value K.new.x #=> :made_in_ractor (reading is fine) K.define_method(:y) { 1 } #=> IsolationError (nobody owns it any more) ``` There is intentionally no API to look up or transfer ownership. * **Class variables** are *not* covered by the relaxation. They are shared across the whole inheritance chain and the class they are physically stored in can migrate over time (`cvar_overtaken`), so no single owner can be defined for them. Writes still require the main Ractor, and additionally must not cross the ownership boundary (the class actually written into must be owned), so class fields keep a single writer. * **Copying a foreign class**: `Class#dup`/`clone` of a class created by another Ractor produces a copy owned by the copying Ractor. It raises if the source's constants or instance variables refer to unshareable objects (they would leak across Ractors); copying classes holding only shareable values works. This gives a mutation-free alternative to monkey-patching: ```ruby S = Class.new; S.const_set(:OK, 1) Ractor.new { k = S.dup; k.define_method(:m) { 1 }; k.new.m }.value #=> 1 ``` * **Top-level class and module names**: `class Foo; end` / `module Foo; end` at the top level of a non-main Ractor writes a constant into `Object`, so both are prohibited. Define them under a module the Ractor creates itself instead (`mod = Module.new; mod.const_set(:Foo, Class.new)`). Integration with `Ruby::Box` is future work. * **Bug fix included**: `cvar_overtaken()` physically deleted a duplicated cvar entry from the `front` class, and that path is reachable from *read* operations (`rb_cvar_find`) -- i.e. a cross-Ractor write triggered by a read. The clean-up is now skipped unless the current Ractor owns `front`. --- ## 7. Incompatibility Only code that modifies classes from a non-main Ractor is affected. Concretely, the following raise `Ractor::IsolationError` where they used to work: * method definition / `alias` / visibility change on a foreign class, including top-level `def` * `include`/`prepend` into a foreign class, `refine` targeting one * setting a **shareable** constant on a foreign class, `remove_const`, `autoload` registration * `Class#inherited` and `const_missing`/`method_missing` hooks which mutate a foreign class Migration is usually "define it before spawning the Ractor" (eager loading), or "create the class inside the Ractor that uses it", or "`dup` the foreign class and modify the copy". Programs which never define classes inside non-main Ractors are unaffected. --- ## 8. Open questions / future work * `freeze` of a foreign class is still allowed. It is a behavior-changing write and should probably be owner-only too. * `Module#set_temporary_name` is not checked yet. * Singleton class ownership is not transferred by `Ractor#send(move: true)`. * Integration with `Ruby::Box`, so that non-main Ractors can define top-level class/module names. * Should the error message for a class owned by the main Ractor say "owned by the main Ractor" rather than "created by another Ractor"? The current wording is confusing for top-level `def`, where the user never mentioned `Object`. --- ## 9. Implementation https://github.com/ruby/ruby/pull/17913 The owner is stored in `rb_classext_t::owner_ractor` as the Ractor object; `0` means the main Ractor, so single-Ractor programs get no additional GC edges. It is marked from the classext and updated on compaction, so the comparison never sees a dangling or reused reference. Method-table checks funnel through `rb_class_modify_check()`; constant/ivar/cvar checks replace the previous `rb_ractor_main_p()` checks in `variable.c`. The class-ivar fast paths in `vm_insnhelper.c` switch from "main Ractor" to "owner Ractor", which keeps them valid because the owner is the only writer and its threads are serialized by the per-Ractor lock. `make btest` (2053 tests), `make test-all` (35797 tests, 0 failures / 0 errors) and `make test-spec` (32628 examples, 0 failures / 0 errors / 0 tagged) all pass. Three tests which relied on cross-Ractor method definition were rewritten so that the owner Ractor performs the definition. ## Notes * Above description is written by Claude code from my explanation. * The biggest motivation is "main-Ractor" exceptions. The concept "owner Ractor for classes/modules" solves this kind of exception. * Another idea for mutating the classes/modules is prohibit them on non-main Ractors because most of classes/modules are defined on the loading time on the main Ractor. However, it also prohibits `Class.new` in non-main Ractors, and it is common programming pattern on Ruby. -- https://bugs.ruby-lang.org/
4 7
0 0
[ruby-core:126583] [Ruby Bug#18473] Raw data in Socket::Option#inspect on Amazon Linux 2
by wanabe (_ wanabe) 06 Sep '26

06 Sep '26
Issue #18473 has been updated by wanabe (_ wanabe). Status changed from Open to Third Party's Issue I was able to reproduce the issue using Vagrant, VirtualBox, `bento/ubuntu-18.04`, and Docker. This issue appears to occur with specific combinations of linux kernel versions and Alpine Linux. Therefore, I believe this is a third-party's issue. Alpine on 4.15.0-206: NG ``` $ vagrant ssh -c 'uname -a; printf "require \"socket\"; tcp_server = TCPServer.new(\"127.0.0.1\", 9000); tcp_server.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true); tcp_server.listen 1024; puts tcp_server.getsockopt(Socket::SOL_TCP, Socket::TCP_INFO).inspect; tcp_server.close" > a.rb; sudo docker run --rm -it -v.:/work ruby:3.1.0-alpine3.15 ruby /work/a.rb' Linux vagrant 4.15.0-206-generic #217-Ubuntu SMP Fri Feb 3 19:10:13 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux #<Socket::Option: INET TCP INFO "\n\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\n\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"> ``` Debian (bullseye) on 4.15.0-206: OK ``` $ vagrant ssh -c 'uname -a; printf "require \"socket\"; tcp_server = TCPServer.new(\"127.0.0.1\", 9000); tcp_server.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true); tcp_server.listen 1024; puts tcp_server.getsockopt(Socket::SOL_TCP, Socket::TCP_INFO).inspect; tcp_server.close" > a.rb; sudo docker run --rm -it -v.:/work ruby:3.1.0-bullseye ruby /work/a.rb' Linux vagrant 4.15.0-206-generic #217-Ubuntu SMP Fri Feb 3 19:10:13 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux #<Socket::Option: INET TCP INFO state=LISTEN ca_state=Open retransmits=0 probes=0 backoff=0 options=0 rto=0.000000s ato=0.000000s snd_mss=0 rcv_mss=0 unacked=0 sacked=128 lost=0 retrans=0 fackets=0 last_data_sent=0.000s last_ack_sent=0.000s last_data_recv=0.000s last_ack_recv=0.000s pmtu=0 rcv_ssthresh=0 rtt=0.000000s rttvar=0.000000s snd_ssthresh=0 snd_cwnd=10 advmss=0 reordering=3 rcv_rtt=0.000000s rcv_space=0 total_retrans=0 (88 bytes too long)> ``` Alpine on 5.4.0-37: OK ``` $ vagrant ssh -c 'uname -a; printf "require \"socket\"; tcp_server = TCPServer.new(\"127.0.0.1\", 9000); tcp_server.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true); tcp_server.listen 1024; puts tcp_server.getsockopt(Socket::SOL_TCP, Socket::TCP_INFO).inspect; tcp_server.close" > a.rb; sudo docker run --rm -it -v.:/work ruby:3.1.0-alpine3.15 ruby /work/a.rb' Linux vagrant 5.4.0-37-generic #41~18.04.1-Ubuntu SMP Mon Jun 8 13:37:29 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux #<Socket::Option: INET TCP INFO state=LISTEN ca_state=Open retransmits=0 probes=0 backoff=0 options=0 rto=0.000000s ato=0.000000s snd_mss=0 rcv_mss=0 unacked=0 sacked=1024 lost=0 retrans=0 fackets=0 last_data_sent=0.000s last_ack_sent=0.000s last_data_recv=0.000s last_ack_recv=0.000s pmtu=0 rcv_ssthresh=0 rtt=0.000000s rttvar=0.000000s snd_ssthresh=0 snd_cwnd=10 advmss=0 reordering=3 rcv_rtt=0.000000s rcv_space=0 total_retrans=0 snd_wnd=0 rcv_ooopack=0> ``` Alpine on 5.3.0-76: NG ``` $ vagrant ssh -c 'printf "system \"uname -a\"; require \"socket\"; tcp_server = TCPServer.new(\"127.0.0.1\", 9000); tcp_server.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true); tcp_server.listen 1024; puts tcp_server.getsockopt(Socket::SOL_TCP, Socket::TCP_INFO).inspect; tcp_server.close" > a.rb; sudo docker run --rm -it -v.:/work ruby:3.1.0-alpine3.15 ruby /work/a.rb' Linux d1e29cb69509 5.3.0-76-generic #72-Ubuntu SMP Thu Jul 15 10:51:23 UTC 2021 x86_64 Linux #<Socket::Option: INET TCP INFO "\n\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\n\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"> ``` ---------------------------------------- Bug #18473: Raw data in Socket::Option#inspect on Amazon Linux 2 https://bugs.ruby-lang.org/issues/18473#change-118793 * Author: driv3r (Leszek Zalewski) * Status: Third Party's Issue * ruby -v: ruby 3.1.0p0 (2021-12-25 revision fb4df44d16) [x86_64-linux-musl] * Backport: 2.6: UNKNOWN, 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN ---------------------------------------- Hello, I found a weird issue when running attached script. Locally on Ubuntu running kernel 5.11, on CI (GitHub Actions) and through docker image (ruby:3.1.0-alpine3.15), the response is as it should be with all data parsed correctly: ``` ruby #<Socket::Option: INET TCP INFO state=LISTEN ca_state=Open retransmits=0 probes=0 backoff=0 options=0 rto=0.000000s ato=0.000000s snd_mss=0 rcv_mss=0 unacked=0 sacked=1024 lost=0 retrans=0 fackets=0 last_data_sent=0.000s last_ack_sent=0.000s last_data_recv=0.000s last_ack_recv=0.000s pmtu=0 rcv_ssthresh=0 rtt=0.000000s rttvar=0.000000s snd_ssthresh=0 snd_cwnd=10 advmss=0 reordering=3 rcv_rtt=0.000000s rcv_space=0 total_retrans=0 (128 bytes too long)> ``` The issue happens when running exactly the same docker image on AWS Fargate, where instead of expected output above, we get ```ruby "#<Socket::Option: INET TCP INFO \"\\n\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\n\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xFF\\xFF\\xFF\\xFF\\xFF\\xFF\\xFF\\xFF\\xFF\\xFF\\xFF\\xFF\\xFF\\xFF\\xFF\\xFF\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\">" ``` After further testing even simple EC2 with Amazon Linux 2 @4.14 kernel gives same output, 5.10 works fine. Steps to reproduce: - boot up EC2 instance with Amazon Linux v2 @ kernel 4.14 (no idea how to run it locally, I might assist if needed) - install docker ```bash sudo yum install docker sudo systemctl start docker.service sudo docker run -it ruby:3.1.0-alpine3.15 ``` - execute the attached script ---Files-------------------------------- test.rb (246 Bytes) -- https://bugs.ruby-lang.org/
1 0
0 0
[ruby-core:126564] [Ruby Bug#22290] Adding instance variables to anonymous object may bloat all future classes
by byroot (Jean Boussier) 06 Sep '26

06 Sep '26
Issue #22290 has been reported by byroot (Jean Boussier). ---------------------------------------- Bug #22290: Adding instance variables to anonymous object may bloat all future classes https://bugs.ruby-lang.org/issues/22290 * Author: byroot (Jean Boussier) * Status: Open * Backport: 3.3: WONTFIX, 3.4: REQUIRED, 4.0: REQUIRED ---------------------------------------- Reproduction script: ```ruby o = Object.new 10.times do |i| o.instance_variable_set("@a_#{i}", 1) end class Test def initialize(a, b) @a = a @b = b end end require 'objspace' p ObjectSpace.memsize_of(Test.new(1, 2)) ``` Expected: ``` 40 # or 32 on 4.1.dev ``` Actual: ``` 160 ``` ### Explanation `Class` records the max number of instance variables any of its instances held (`max_iv_count`). When inheriting from a class, `max_iv_count` is copied in the child class. If you add instance variables to an instance of `Object`, any future direct children of `Object` will start with a larger than needed `max_iv_count` and will cause Ruby to potentially allocate much larger objects than needed. All versions since 3.2 are affected, the issue was fixed on master a couple months. I believe we should backport the fix (I'm working on it), because otherwise some un-proper code running early in the process life-cycle can cause significant memory waste. -- https://bugs.ruby-lang.org/
2 2
0 0
[ruby-core:126513] [Ruby Bug#22269] `Coverage.line_stub` clobbers already-collected coverage data
by mugikurash (Shuta Mugikura) 06 Sep '26

06 Sep '26
Issue #22269 has been reported by mugikurash (Shuta Mugikura). ---------------------------------------- Bug #22269: `Coverage.line_stub` clobbers already-collected coverage data https://bugs.ruby-lang.org/issues/22269 * Author: mugikurash (Shuta Mugikura) * Status: Open * ruby -v: ruby 4.1.0dev (2026-08-27T01:47:31Z master 5ee95de080) +PRISM [arm64-darwin25] * Backport: 3.3: UNKNOWN, 3.4: UNKNOWN, 4.0: UNKNOWN ---------------------------------------- `Coverage.line_stub` compiles the file with `compile_file` only to read the ISeq's `trace_points`. Since https://bugs.ruby-lang.org/issues/22018 `compile_file` registers the file into the coverage table, so calling `line_stub` while `Coverage` is running resets the file's already-collected counters. This also reproduces on 3.4 and 4.0, so I would like it backported. Reproduction: ```ruby require "coverage" f = File.expand_path("m.rb") File.write(f, "def m\n :ok\nend\n") Coverage.start require f m p Coverage.peek_result[f] Coverage.line_stub(f) p Coverage.peek_result[f] m p Coverage.result[f] ``` Expected: ``` [1, 1, nil] [1, 1, nil] [1, 2, nil] ``` Actual: ``` [1, 1, nil] [0, 0, nil] [0, 0, nil] ``` -- https://bugs.ruby-lang.org/
2 2
0 0
  • ← Newer
  • 1
  • ...
  • 8
  • 9
  • 10
  • 11
  • 12
  • Older →

HyperKitty Powered by HyperKitty version 1.3.12.