mirror of
https://github.com/ruby/ruby.git
synced 2026-01-27 20:44:20 +00:00
[ruby/timeout] Make Timeout.timeout work in a trap handler on CRuby
* Fixes https://github.com/ruby/timeout/issues/17 https://github.com/ruby/timeout/commit/1a499a8f96
This commit is contained in:
parent
9865048a34
commit
b49ff7cc70
@ -123,7 +123,7 @@ module Timeout
|
||||
# In that case, just return and let the main thread create the Timeout thread.
|
||||
return if @timeout_thread_mutex.owned?
|
||||
|
||||
@timeout_thread_mutex.synchronize do
|
||||
Sync.synchronize @timeout_thread_mutex do
|
||||
unless @timeout_thread&.alive?
|
||||
@timeout_thread = create_timeout_thread
|
||||
end
|
||||
@ -132,7 +132,7 @@ module Timeout
|
||||
end
|
||||
|
||||
def add_request(request)
|
||||
@queue_mutex.synchronize do
|
||||
Sync.synchronize @queue_mutex do
|
||||
@queue << request
|
||||
@condvar.signal
|
||||
end
|
||||
@ -153,6 +153,7 @@ module Timeout
|
||||
@done = false # protected by @mutex
|
||||
end
|
||||
|
||||
# Only called by the timeout thread, so does not need Sync.synchronize
|
||||
def done?
|
||||
@mutex.synchronize do
|
||||
@done
|
||||
@ -163,6 +164,7 @@ module Timeout
|
||||
now >= @deadline
|
||||
end
|
||||
|
||||
# Only called by the timeout thread, so does not need Sync.synchronize
|
||||
def interrupt
|
||||
@mutex.synchronize do
|
||||
unless @done
|
||||
@ -173,13 +175,33 @@ module Timeout
|
||||
end
|
||||
|
||||
def finished
|
||||
@mutex.synchronize do
|
||||
Sync.synchronize @mutex do
|
||||
@done = true
|
||||
end
|
||||
end
|
||||
end
|
||||
private_constant :Request
|
||||
|
||||
module Sync
|
||||
# Calls mutex.synchronize(&block) but if that fails on CRuby due to being in a trap handler,
|
||||
# run mutex.synchronize(&block) in a separate Thread instead.
|
||||
def self.synchronize(mutex, &block)
|
||||
begin
|
||||
mutex.synchronize(&block)
|
||||
rescue ThreadError => e
|
||||
raise e unless e.message == "can't be called from trap context"
|
||||
# Workaround CRuby issue https://bugs.ruby-lang.org/issues/19473
|
||||
# which raises on Mutex#synchronize in trap handler.
|
||||
# It's expensive to create a Thread just for this,
|
||||
# but better than failing.
|
||||
Thread.new {
|
||||
mutex.synchronize(&block)
|
||||
}.join
|
||||
end
|
||||
end
|
||||
end
|
||||
private_constant :Sync
|
||||
|
||||
# :startdoc:
|
||||
|
||||
# Perform an operation in a block, raising an exception if it takes longer than
|
||||
|
||||
@ -416,4 +416,33 @@ class TestTimeout < Test::Unit::TestCase
|
||||
assert_equal :ok, r
|
||||
end;
|
||||
end if defined?(::Ractor) && RUBY_VERSION >= '4.0'
|
||||
|
||||
def test_timeout_in_trap_handler
|
||||
# https://github.com/ruby/timeout/issues/17
|
||||
|
||||
# Test as if this was the first timeout usage
|
||||
kill_timeout_thread
|
||||
|
||||
rd, wr = IO.pipe
|
||||
|
||||
trap("SIGUSR1") do
|
||||
begin
|
||||
Timeout.timeout(0.1) do
|
||||
sleep 1
|
||||
end
|
||||
rescue Timeout::Error
|
||||
wr.write "OK"
|
||||
wr.close
|
||||
else
|
||||
wr.write "did not raise"
|
||||
ensure
|
||||
wr.close
|
||||
end
|
||||
end
|
||||
|
||||
Process.kill :USR1, Process.pid
|
||||
|
||||
assert_equal "OK", rd.read
|
||||
rd.close
|
||||
end
|
||||
end
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user