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]
This commit is contained in:
parent
27688b6a1d
commit
76a8c963c7
@ -2783,4 +2783,59 @@ EOS
|
|||||||
assert_operator(GC.stat(:total_freed_pages), :>, 0)
|
assert_operator(GC.stat(:total_freed_pages), :>, 0)
|
||||||
end;
|
end;
|
||||||
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
|
end
|
||||||
|
Loading…
x
Reference in New Issue
Block a user