
Issue #21504 has been updated by luke-gru (Luke Gruber). This is actually a more pervasive problem than I first realized, because only sometimes does `IO#read` register with the timer thread. With a plain `IO#read` without arguments, it does not wake the timer thread and can block the nt forever as well. ---------------------------------------- Bug #21504: [Ractor] Process.waitpid blocks ractor, new NT doesn't pick up other ractors https://bugs.ruby-lang.org/issues/21504#change-114121 * Author: luke-gru (Luke Gruber) * Status: Open * Assignee: ractor * Backport: 3.2: UNKNOWN, 3.3: UNKNOWN, 3.4: UNKNOWN ---------------------------------------- The following code hangs when run with `RUBY_MAX_CPU=2 make run`: Note: `RUBY_MAX_CPU` is set to 2 so that only 1 non-main ractor can run at once. test.rb: ```ruby rs = [] 2.times do |i| rs << Ractor.new(i) do |i| if i == 0 io = IO.popen("ruby -e 'sleep'") Process.wait(io.pid) # block forever else sleep 1 # make sure first ractor blocks forever first $stderr.puts "Running r #{i}" 100_000.times do [nil] * 1_000 end $stderr.puts "done r #{i}" end end end while rs.size == 2 r, obj = Ractor.select(*rs) rs.delete(r) end ``` The timer thread should create a new NT to compensate for the dedicated task, and the new NT should be able to pick up the other runnable ractor. In contrast, the following works fine: ```ruby rs = [] 2.times do |i| rs << Ractor.new(i) do |i| if i == 0 r, w = IO.pipe r.read(1) # block forever else sleep 1 # make sure first ractor blocks forever first $stderr.puts "Running r #{i}" 100_000.times do [nil] * 1_000 end $stderr.puts "done r #{i}" end end end while rs.size == 2 r, obj = Ractor.select(*rs) rs.delete(r) end ``` -- https://bugs.ruby-lang.org/