[ruby-core:117719] [Ruby master Bug#20456] Hash can get stuck marked as iterating through process forking
From:
"byroot (Jean Boussier) via ruby-core" <ruby-core@...>
Date:
2024-04-26 07:41:12 UTC
List:
ruby-core #117719
Issue #20456 has been updated by byroot (Jean Boussier).
> but it sounds to me like it can't.
It could, and there's precedent. For instance Ruby unlock mutexes owned by dead threads.
This would require to keep a list of the threads that incremented a hash's `iterlevel`, so definitely not free, but perhaps acceptable overhead. Would be worth trying IMO.
One possible implementation could be for each threads (or fiber?) to have a stack of `VALUE`, in which to store the threads for which it bumped the iterlevel. The overhead would be an extra RArray `push/pop` when starting and exiting iterating on a Hash, which I think may not matter that much in the grand scheme of things.
----------------------------------------
Bug #20456: Hash can get stuck marked as iterating through process forking
https://bugs.ruby-lang.org/issues/20456#change-108127
* 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/