summaryrefslogtreecommitdiff
path: root/test/ruby
diff options
context:
space:
mode:
authorLuke Gruber <luke.gruber@shopify.com>2026-02-06 14:26:07 -0500
committerGitHub <noreply@github.com>2026-02-06 14:26:07 -0500
commit7b3370a5579956404d742a2e104d72e7c89480e4 (patch)
treeaef21dbdc1071771da877a752d9e8a25bdf315a5 /test/ruby
parent130c37d6e60b0b654b93cb09c37b18718e35c0b9 (diff)
Wake timer to create new SNT when needed for dedicated task (#16009)
When removing a thread from `running_threads`, if we're on a shared native thread and we're running a dedicated task, we need to wake the timer thread so it can create a new SNT if necessary. We only do this if it's waiting forever without the 10ms quantum timeout for now, because max 10ms of wait is considered "good enough". In the future, perhaps we can force the timer thread to wake if this becomes an issue (`timer_thread_wakeup_force`). Fixes [Bug #21504]
Diffstat (limited to 'test/ruby')
-rw-r--r--test/ruby/test_ractor.rb39
1 files changed, 39 insertions, 0 deletions
diff --git a/test/ruby/test_ractor.rb b/test/ruby/test_ractor.rb
index 6ae511217a..d449f9f814 100644
--- a/test/ruby/test_ractor.rb
+++ b/test/ruby/test_ractor.rb
@@ -201,6 +201,45 @@ class TestRactor < Test::Unit::TestCase
RUBY
end
+ def test_timer_thread_create_snt_for_dedicated_task
+ omit "timer thread works differently" if windows?
+ omit "test relies on this as a best-effort safety mechanism" unless defined?(Process::WNOHANG)
+ assert_separately([{ "RUBY_MAX_CPU" => "2" }], <<~'RUBY', timeout: 30)
+ $VERBOSE = nil
+ CHILD_PID = 0
+
+ rs = []
+ 2.times do |i|
+ rs << Ractor.new(i) do |j|
+ if j == 0
+ pid = spawn("sleep 60", close_others: true)
+ Object.const_set(:CHILD_PID, pid)
+ Process.waitpid(pid) # block forever (dedicated task)
+ else
+ while CHILD_PID == 0
+ sleep 1 # make sure first ractor blocks forever first (this is what we're testing)
+ end
+ 1_000.times do
+ [nil] * 100
+ end
+ end
+ end
+ end
+
+ rs.last.join
+ begin
+ result = Process.waitpid(CHILD_PID, Process::WNOHANG)
+ rescue Errno::ECHILD, Errno::ESRCH
+ # If it's somehow not a child (not running?), don't send it a signal
+ else
+ if result.nil?
+ Process.kill('KILL', CHILD_PID) rescue nil
+ end
+ end
+ rs.first.join # reap
+ RUBY
+ end
+
def test_symbol_proc_is_shareable
pr = :symbol.to_proc
assert_make_shareable(pr)