Bug #22434
closed"No live threads left. Deadlock?" when running system() or spawn in Ractors
Description
Non-main Ractors don't support fork(), but do support system() and spawn(). They can end up deadlocked inside the prefork method when trying to flush stdio.
$stdout = File.open(File::NULL, "w")
writer = Thread.new { loop { $stdout.write("x" * 100 + "\n"); $stdout.flush } }
r = Ractor.new { 500.times { system("true") }; :done }
raise unless :done == r.value
writer.kill.join
❯ ruby -v test.rb
ruby 4.0.7 (2026-09-15 revision 229531a6cf) +PRISM [arm64-darwin25]
test.rb:3: warning: Ractor API is experimental and may change in future versions of Ruby.
#<Thread:0x000000010c1f62a8 run> terminated with exception (report_on_exception is true):
test.rb:3:in 'Kernel#system': No live threads left. Deadlock? (fatal)
1 threads, 1 sleeps current:0x00000007e696b800 main thread:0x00000007e696b800
* #<Thread:0x000000010c1f62a8 sleep_forever>
rb_thread_t:0x00000007e696b800 native:0x000000016bcef000 int:0
test.rb:3:in 'Kernel#system'
test.rb:3:in 'block (2 levels) in <main>'
test.rb:3:in 'Integer#times'
test.rb:3:in 'block in <main>'
...
Updated by jhawthorn (John Hawthorn) 18 days ago
- Backport changed from 3.3: UNKNOWN, 3.4: UNKNOWN, 4.0: REQUIRED to 3.3: WONTFIX, 3.4: WONTFIX, 4.0: REQUIRED
Updated by jhawthorn (John Hawthorn) 16 days ago
- Status changed from Open to Closed
Applied in changeset git|28f2eae4cf7681be36c3011a423154e45d21721c.
Flush the current Ractor's stdout/stderr before spawning
prefork() flushed rb_stdout and rb_stderr, which are the main Ractor's IOs.
From another Ractor that takes the write_lock of an IO the Ractor does not
own. When a main Ractor thread holds it (any thread logging to $stdout will
do), the spawning Ractor's only thread sleeps on the mutex and
rb_check_deadlock, which only looks at the Ractor's own threads, raises
"No live threads left. Deadlock?" (fatal) from Kernel#spawn/system/popen.
Use rb_ractor_stdout()/rb_ractor_stderr() so each Ractor flushes its own.
[Bug #22434]