Skip to content

Commit 55d7c84

Browse files
authored
Compatibility with Fiber scheduler. (#97)
[Bug #21947]
1 parent 35504ba commit 55d7c84

File tree

2 files changed

+88
-13
lines changed

2 files changed

+88
-13
lines changed

lib/timeout.rb

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -282,19 +282,21 @@ def self.timeout(sec, klass = nil, message = nil, &block) #:yield: +sec+
282282
message ||= "execution expired"
283283

284284
if Fiber.respond_to?(:current_scheduler) && (scheduler = Fiber.current_scheduler)&.respond_to?(:timeout_after)
285-
return scheduler.timeout_after(sec, klass || Error, message, &block)
286-
end
287-
288-
state = State.instance
289-
state.ensure_timeout_thread_created
290-
291-
perform = Proc.new do |exc|
292-
request = Request.new(Thread.current, sec, exc, message)
293-
state.add_request(request)
294-
begin
295-
return yield(sec)
296-
ensure
297-
request.finished
285+
perform = Proc.new do |exc|
286+
scheduler.timeout_after(sec, exc, message, &block)
287+
end
288+
else
289+
state = State.instance
290+
state.ensure_timeout_thread_created
291+
292+
perform = Proc.new do |exc|
293+
request = Request.new(Thread.current, sec, exc, message)
294+
state.add_request(request)
295+
begin
296+
return yield(sec)
297+
ensure
298+
request.finished
299+
end
298300
end
299301
end
300302

test/test_timeout.rb

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,4 +459,77 @@ def test_timeout_in_trap_handler
459459
trap(signal, original_handler)
460460
end
461461
end
462+
463+
if Fiber.respond_to?(:current_scheduler)
464+
# Stubs Fiber.current_scheduler for the duration of the block, then restores it.
465+
def with_mock_scheduler(mock)
466+
original = Fiber.method(:current_scheduler)
467+
Fiber.define_singleton_method(:current_scheduler) { mock }
468+
begin
469+
yield
470+
ensure
471+
Fiber.define_singleton_method(:current_scheduler, original)
472+
end
473+
end
474+
475+
def test_fiber_scheduler_delegates_to_timeout_after
476+
received = nil
477+
mock = Object.new
478+
mock.define_singleton_method(:timeout_after) do |sec, exc, msg, &blk|
479+
received = [sec, exc, msg]
480+
blk.call(sec)
481+
end
482+
483+
with_mock_scheduler(mock) do
484+
assert_equal :ok, Timeout.timeout(5) { :ok }
485+
end
486+
487+
assert_equal 5, received[0]
488+
assert_instance_of Timeout::ExitException, received[1], "scheduler should receive an ExitException instance when no klass given"
489+
assert_equal "execution expired", received[2]
490+
end
491+
492+
def test_fiber_scheduler_delegates_to_timeout_after_with_custom_exception
493+
custom_error = Class.new(StandardError)
494+
received = nil
495+
mock = Object.new
496+
mock.define_singleton_method(:timeout_after) do |sec, exc, msg, &blk|
497+
received = [sec, exc, msg]
498+
blk.call(sec)
499+
end
500+
501+
with_mock_scheduler(mock) do
502+
assert_equal :ok, Timeout.timeout(5, custom_error, "custom message") { :ok }
503+
end
504+
505+
assert_equal [5, custom_error, "custom message"], received
506+
end
507+
508+
def test_fiber_scheduler_timeout_raises_timeout_error
509+
mock = Object.new
510+
mock.define_singleton_method(:timeout_after) do |sec, exc, msg, &blk|
511+
raise exc # simulate timeout firing
512+
end
513+
514+
with_mock_scheduler(mock) do
515+
assert_raise(Timeout::Error) do
516+
Timeout.timeout(5) { :should_not_reach }
517+
end
518+
end
519+
end
520+
521+
def test_fiber_scheduler_timeout_raises_custom_error
522+
custom_error = Class.new(StandardError)
523+
mock = Object.new
524+
mock.define_singleton_method(:timeout_after) do |sec, exc, msg, &blk|
525+
raise exc, msg
526+
end
527+
528+
with_mock_scheduler(mock) do
529+
assert_raise_with_message(custom_error, "custom message") do
530+
Timeout.timeout(5, custom_error, "custom message") { :should_not_reach }
531+
end
532+
end
533+
end
534+
end
462535
end

0 commit comments

Comments
 (0)