[ruby-core:117713] [Ruby master Bug#20456] Hash can get stuck marked as iterating through process forking
From:
"Eregon (Benoit Daloze) via ruby-core" <ruby-core@...>
Date:
2024-04-25 19:56:29 UTC
List:
ruby-core #117713
Issue #20456 has been updated by Eregon (Benoit Daloze).
I would think this is a general pitfall of `fork()`, it can leave things in other threads in a random inconsistent and irrecoverable state.
IMO it's a mistake that `fork()` doesn't copy all threads to the new process, but it is what it is.
Maybe this specific issue can be worked around in CRuby, but it sounds to me like it can't.
So the best is to ensure a single Ruby-level thread when calling `Process.fork`.
Maybe `Process.fork` should warn or error if that's not the case.
----------------------------------------
Bug #20456: Hash can get stuck marked as iterating through process forking
https://bugs.ruby-lang.org/issues/20456#change-108122
* 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, #<Process::Status: pid 34450 exit 1>]
```
## 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, #<Process::Status: pid 17809 exit 1>]
```
# 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/