Skip to content

Commit eafbc16

Browse files
committed
Add spec for interrupting the main thread in rb_thread_call_without_gvl() with a signal
1 parent dd4bb57 commit eafbc16

File tree

1 file changed

+36
-2
lines changed

1 file changed

+36
-2
lines changed

spec/ruby/optional/capi/thread_spec.rb

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,13 +102,13 @@ def call_capi_rb_thread_wakeup
102102
end
103103

104104
describe "rb_thread_call_without_gvl" do
105-
it "runs a C function with the global lock unlocked" do
105+
it "runs a C function with the global lock unlocked and can be woken by Thread#wakeup" do
106106
thr = Thread.new do
107107
@t.rb_thread_call_without_gvl
108108
end
109109

110110
# Wait until it's blocking...
111-
Thread.pass while thr.status and thr.status != "sleep"
111+
Thread.pass until thr.stop?
112112

113113
# The thread status is set to sleep by rb_thread_call_without_gvl(),
114114
# but the thread might not be in the blocking read(2) yet, so wait a bit.
@@ -121,6 +121,40 @@ def call_capi_rb_thread_wakeup
121121
thr.value.should be_true
122122
end
123123

124+
platform_is_not :windows do
125+
it "runs a C function with the global lock unlocked and can be woken by a signal" do
126+
# Ruby signal handlers run on the main thread, so we need to reverse roles here and have a thread interrupt us
127+
thr = Thread.current
128+
thr.should == Thread.main
129+
130+
going_to_block = false
131+
interrupter = Thread.new do
132+
# Wait until it's blocking...
133+
Thread.pass until going_to_block and thr.stop?
134+
135+
# The thread status is set to sleep by rb_thread_call_without_gvl(),
136+
# but the thread might not be in the blocking read(2) yet, so wait a bit.
137+
sleep 0.1
138+
139+
# Wake it up by sending a signal
140+
done = false
141+
prev_handler = Signal.trap(:HUP) { done = true }
142+
begin
143+
Process.kill :HUP, Process.pid
144+
sleep 0.001 until done
145+
ensure
146+
Signal.trap(:HUP, prev_handler)
147+
end
148+
end
149+
150+
going_to_block = true
151+
# Make sure it stopped and we got a proper value
152+
@t.rb_thread_call_without_gvl.should be_true
153+
154+
interrupter.join
155+
end
156+
end
157+
124158
guard -> { platform_is :mingw and ruby_version_is ""..."2.7" } do
125159
it "runs a C function with the global lock unlocked and unlocks IO with the generic RUBY_UBF_IO" do
126160
thr = Thread.new do

0 commit comments

Comments
 (0)