diff options
| author | KJ Tsanaktsidis <kj@kjtsanaktsidis.id.au> | 2023-08-18 23:19:21 +1000 |
|---|---|---|
| committer | KJ Tsanaktsidis <kj@kjtsanaktsidis.id.au> | 2024-01-11 13:12:17 +1100 |
| commit | 76a8c963c7ad975b7bbfc1c4979bf7a2de15af27 (patch) | |
| tree | 37516471237f0ab125e82bf99a59f5026da37e49 /test/ruby | |
| parent | 27688b6a1df7b58b539165c7d17b359db5142bd7 (diff) | |
Add a test for what happens with concurent calls to waitpid
Ruby 3.1 and 3.2 have a bug in their _implementation_, for which I'm
backporting a fix. However, the current development branch doesn't have
the issue (because the MJIT -> RJIT change refactored how waitpid worked
substantially). I do however want to commit the test which verifies
that waitpid works properly on master.
[Fixes #19387]
Diffstat (limited to 'test/ruby')
| -rw-r--r-- | test/ruby/test_process.rb | 55 |
1 files changed, 55 insertions, 0 deletions
diff --git a/test/ruby/test_process.rb b/test/ruby/test_process.rb index 188ef75fae..edee624665 100644 --- a/test/ruby/test_process.rb +++ b/test/ruby/test_process.rb @@ -2783,4 +2783,59 @@ EOS assert_operator(GC.stat(:total_freed_pages), :>, 0) end; end + + def test_concurrent_group_and_pid_wait + # Use a pair of pipes that will make long_pid exit when this test exits, to avoid + # leaking temp processes. + long_rpipe, long_wpipe = IO.pipe + short_rpipe, short_wpipe = IO.pipe + # This process should run forever + long_pid = fork do + [short_rpipe, short_wpipe, long_wpipe].each(&:close) + long_rpipe.read + end + # This process will exit + short_pid = fork do + [long_rpipe, long_wpipe, short_wpipe].each(&:close) + short_rpipe.read + end + t1, t2, t3 = nil + EnvUtil.timeout(5) do + t1 = Thread.new do + Process.waitpid long_pid + end + # Wait for us to be blocking in a call to waitpid2 + Thread.pass until t1.stop? + short_wpipe.close # Make short_pid exit + + # The short pid has exited, so -1 should pick that up. + assert_equal short_pid, Process.waitpid(-1) + + # Terminate t1 for the next phase of the test. + t1.kill + t1.join + + t2 = Thread.new do + Process.waitpid -1 + rescue Errno::ECHILD + nil + end + Thread.pass until t2.stop? + t3 = Thread.new do + Process.waitpid long_pid + rescue Errno::ECHILD + nil + end + Thread.pass until t3.stop? + + # it's actually nondeterministic which of t2 or t3 will receive the wait (this + # nondeterminism comes from the behaviour of the underlying system calls) + long_wpipe.close + assert_equal [long_pid], [t2, t3].map(&:value).compact + end + ensure + [t1, t2, t3].each { _1&.kill rescue nil } + [t1, t2, t3].each { _1&.join rescue nil } + [long_rpipe, long_wpipe, short_rpipe, short_wpipe].each { _1&.close rescue nil } + end if defined?(fork) end |
