From: "Eregon (Benoit Daloze) via ruby-core" Date: 2024-04-26T09:54:42+00:00 Subject: [ruby-core:117724] [Ruby master Bug#20456] Hash can get stuck marked as iterating through process forking Issue #20456 has been updated by Eregon (Benoit Daloze). > I don't think we should shy away from making it easier to use and more robust when we can. The best way to do that IMO is warn/error on fork when there are multiple Ruby Threads alive. > It could, and there's precedent. For instance Ruby unlock mutexes owned by dead threads. It is a measurable overhead on Mutex performance actually (at least on TruffleRuby), and of course it's completely unsound, whatever was being protected under `Mutex#synchronize` is now in an inconsistent state and the next operation on it could very well segfault, race, raise, etc. I think we should remove that, not add more hacks for something which is intrinsically unsafe. If people want to fork and there are multiple Ruby Threads around, they should terminate those threads cleanly first. ---------------------------------------- Bug #20456: Hash can get stuck marked as iterating through process forking https://bugs.ruby-lang.org/issues/20456#change-108132 * Author: blowfishpro (Talia Wong) * Status: Open * ruby -v: 3.3.0 * Backport: 3.1: UNKNOWN, 3.2: UNKNOWN, 3.3: UNKNOWN ---------------------------------------- # Steps to Reproduce 1. Iterate over a hash 1. While that iteration is happening, fork the process a. This should be done in a way that causes the iteration to never finish from the child process's view, e.g. fork with a block, or iteration is happening in a different thread than fork 1. Attempt to add a new key to the hash in the child process a. This can be before or after the iteration finishes in the parent process, doesn't matter # Observed Behavior The child process can never add a new key to the hash, always fails with `RuntimeError: can't add a new key into hash during iteration` # Desired The hash is no longer iterating in the child process, so it can be modified as needed # Examples ## With threads: ```ruby h = { a: 1 } t = Thread.new do sleep 0.05 pid = fork do sleep 0.1 puts 'child: before' h[:b] = 2 puts 'child: after' end Process.wait2(pid) end puts 'parent: before' h.each do sleep 0.1 end puts 'parent: after' puts t.join.value.inspect ``` produces: ``` parent: before parent: after child: before can't add a new key into hash during iteration (RuntimeError) [34450, #] ``` ## Without threads: ``` ruby h = { a: 1 } pid = nil puts 'parent: before' h.each do pid = fork do sleep 0.05 puts 'child: before' h[:b] = 2 puts 'child: after' end end puts 'parent: after' puts Process.wait2(pid).inspect ``` produces: ``` parent: before parent: after child: before can't add a new key into hash during iteration (RuntimeError) [17809, #] ``` # Platform information This behavior has been observed in the following environments - Ruby 3.3.0 on Mac OS 14.4.1 (Apple M1 Max) installed via [asdf](https://asdf-vm.com/) - Ruby 2.7.5 on Mac OS 14.4.1 (Apple M1 Max) installed via [asdf](https://asdf-vm.com/) - Ruby 3.2.3 on RockyLinux 8.4 (x86_64) installed from [Fullstaq](https://fullstaqruby.org/) -- https://bugs.ruby-lang.org/ ______________________________________________ ruby-core mailing list -- ruby-core@ml.ruby-lang.org To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/