Bug #21812
closedKernel#sleep without arguments returns immediately when subprocess exits in another thread (regression in Ruby 4.0)
Description
Description
In Ruby 4.0.0, Kernel#sleep without arguments returns 0 immediately when a subprocess (spawned via backticks in another thread) exits. This is a regression from Ruby 3.4.8 where sleep blocks indefinitely as expected.
Note: sleep(N) with an argument is not affected.
Steps to reproduce
# test_sleep.rb
Thread.new do
sleep 0.3
`echo hello` # Spawns subprocess
puts "Subprocess exited"
end
puts "Main thread sleeping..."
result = sleep # Should block forever
puts "sleep returned: #{result.inspect}"
Run with:
$ ruby test_sleep.rb
Actual result (Ruby 4.0.0):
Main thread sleeping...
Subprocess exited
sleep returned: 0
The program exits immediately after the subprocess completes.
Expected result (Ruby 3.4.8 behavior):
Main thread sleeping...
Subprocess exited
(process blocks indefinitely until interrupted by signal)
The program should block forever until explicitly interrupted by SIGINT/SIGTERM.
Updated by mame (Yusuke Endoh) about 1 month ago
- Status changed from Open to Assigned
- Assignee set to luke-gru (Luke Gruber)
Thanks for the nice catch.
git bisect points to 8d8159e7d87e4fd1594ce2fad3d2653e47fb1026. The changes in that commit seem relevant to this issue.
@luke-gru (Luke Gruber) Could you take a look?
Updated by luke-gru (Luke Gruber) 25 days ago
ยท Edited
@mame (Yusuke Endoh) Yes no problem, I have a fix coming. Thanks for the bisect btw, it saved me quite a bit of time ๐
Updated by Anonymous 25 days ago
- Status changed from Assigned to Closed
Applied in changeset git|7e81bf5c0c8f43602e6d901f4253dca2f3d71745.
Fix sleep spurious wakeup from sigchld (#15802)
When sleeping with sleep, currently the main thread can get woken up from sigchld
from any thread (subprocess exited). The timer thread wakes up the main thread when this
happens, as it checks for signals. The main thread then executes the ruby sigchld handler
if one is registered and is supposed to go back to sleep immediately. This is not ideal but
it's the way it's worked for a while. In commit 8d8159e7d8 I added writes to th->status
before and after wait_running_turn in thread_sched_to_waiting_until_wakeup, which is
called from sleep. This is usually the right way to set the thread's status, but sleep
is an exception because the writes to th->status are done in sleep_forever. There's a
loop that checks th->status in sleep_forever. When the main thread got woken up from
sigchld it saw the changed th->status and continued to run the main thread instead of
going back to sleep.
The following script shows the error. It was returning instead of sleeping forever.
t = Thread.new do
sleep 0.3
`echo hello` # Spawns subprocess
puts "Subprocess exited"
end
puts "Main thread sleeping..."
result = sleep # Should block forever
puts "sleep returned: #{result.inspect}"
Fixes [Bug #21812]
Updated by jhawthorn (John Hawthorn) 25 days ago
- Backport changed from 3.2: UNKNOWN, 3.3: UNKNOWN, 3.4: UNKNOWN, 4.0: UNKNOWN to 3.2: DONTNEED, 3.3: DONTNEED, 3.4: DONTNEED, 4.0: REQUIRED
Updated by k0kubun (Takashi Kokubun) 17 days ago
- Backport changed from 3.2: DONTNEED, 3.3: DONTNEED, 3.4: DONTNEED, 4.0: REQUIRED to 3.2: DONTNEED, 3.3: DONTNEED, 3.4: DONTNEED, 4.0: DONE
ruby_4_0 ac596948d4008c6e117449786c62de4e45e434bf merged revision(s) 7e81bf5c0c8f43602e6d901f4253dca2f3d71745.