summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authornagachika <nagachika@ruby-lang.org>2026-05-09 15:09:56 +0900
committernagachika <nagachika@ruby-lang.org>2026-05-09 15:09:56 +0900
commit6eeeb07e273c069273bcd930a612a0c960668a11 (patch)
treec493e8052b0e69f4b908f44ed6c38b5ac43032f2
parent8a434effcafaa4c2f32170a0003d3c1219110890 (diff)
merge revision(s) f315d250b44e75a1a69f4a05b293dcc701377689: [Backport #21947]
[PATCH] [ruby/timeout] Compatibility with Fiber scheduler. (https://github.com/ruby/timeout/pull/97) [Bug #21947] https://github.com/ruby/timeout/commit/55d7c84b50
-rw-r--r--lib/timeout.rb28
-rw-r--r--test/test_timeout.rb73
-rw-r--r--version.h2
3 files changed, 89 insertions, 14 deletions
diff --git a/lib/timeout.rb b/lib/timeout.rb
index 4fd1fa46da..6218b1969e 100644
--- a/lib/timeout.rb
+++ b/lib/timeout.rb
@@ -171,20 +171,22 @@ module Timeout
message ||= "execution expired"
if Fiber.respond_to?(:current_scheduler) && (scheduler = Fiber.current_scheduler)&.respond_to?(:timeout_after)
- return scheduler.timeout_after(sec, klass || Error, message, &block)
- end
-
- Timeout.ensure_timeout_thread_created
- perform = Proc.new do |exc|
- request = Request.new(Thread.current, sec, exc, message)
- QUEUE_MUTEX.synchronize do
- QUEUE << request
- CONDVAR.signal
+ perform = Proc.new do |exc|
+ scheduler.timeout_after(sec, exc, message, &block)
end
- begin
- return yield(sec)
- ensure
- request.finished
+ else
+ Timeout.ensure_timeout_thread_created
+ perform = Proc.new do |exc|
+ request = Request.new(Thread.current, sec, exc, message)
+ QUEUE_MUTEX.synchronize do
+ QUEUE << request
+ CONDVAR.signal
+ end
+ begin
+ return yield(sec)
+ ensure
+ request.finished
+ end
end
end
diff --git a/test/test_timeout.rb b/test/test_timeout.rb
index 01156867b0..7809fdd68f 100644
--- a/test/test_timeout.rb
+++ b/test/test_timeout.rb
@@ -274,4 +274,77 @@ class TestTimeout < Test::Unit::TestCase
}.join
end;
end
+
+ if Fiber.respond_to?(:current_scheduler)
+ # Stubs Fiber.current_scheduler for the duration of the block, then restores it.
+ def with_mock_scheduler(mock)
+ original = Fiber.method(:current_scheduler)
+ Fiber.define_singleton_method(:current_scheduler) { mock }
+ begin
+ yield
+ ensure
+ Fiber.define_singleton_method(:current_scheduler, original)
+ end
+ end
+
+ def test_fiber_scheduler_delegates_to_timeout_after
+ received = nil
+ mock = Object.new
+ mock.define_singleton_method(:timeout_after) do |sec, exc, msg, &blk|
+ received = [sec, exc, msg]
+ blk.call(sec)
+ end
+
+ with_mock_scheduler(mock) do
+ assert_equal :ok, Timeout.timeout(5) { :ok }
+ end
+
+ assert_equal 5, received[0]
+ assert_instance_of Timeout::ExitException, received[1], "scheduler should receive an ExitException instance when no klass given"
+ assert_equal "execution expired", received[2]
+ end
+
+ def test_fiber_scheduler_delegates_to_timeout_after_with_custom_exception
+ custom_error = Class.new(StandardError)
+ received = nil
+ mock = Object.new
+ mock.define_singleton_method(:timeout_after) do |sec, exc, msg, &blk|
+ received = [sec, exc, msg]
+ blk.call(sec)
+ end
+
+ with_mock_scheduler(mock) do
+ assert_equal :ok, Timeout.timeout(5, custom_error, "custom message") { :ok }
+ end
+
+ assert_equal [5, custom_error, "custom message"], received
+ end
+
+ def test_fiber_scheduler_timeout_raises_timeout_error
+ mock = Object.new
+ mock.define_singleton_method(:timeout_after) do |sec, exc, msg, &blk|
+ raise exc # simulate timeout firing
+ end
+
+ with_mock_scheduler(mock) do
+ assert_raise(Timeout::Error) do
+ Timeout.timeout(5) { :should_not_reach }
+ end
+ end
+ end
+
+ def test_fiber_scheduler_timeout_raises_custom_error
+ custom_error = Class.new(StandardError)
+ mock = Object.new
+ mock.define_singleton_method(:timeout_after) do |sec, exc, msg, &blk|
+ raise exc, msg
+ end
+
+ with_mock_scheduler(mock) do
+ assert_raise_with_message(custom_error, "custom message") do
+ Timeout.timeout(5, custom_error, "custom message") { :should_not_reach }
+ end
+ end
+ end
+ end
end
diff --git a/version.h b/version.h
index 46182a0b6d..0796278b85 100644
--- a/version.h
+++ b/version.h
@@ -11,7 +11,7 @@
# define RUBY_VERSION_MINOR RUBY_API_VERSION_MINOR
#define RUBY_VERSION_TEENY 9
#define RUBY_RELEASE_DATE RUBY_RELEASE_YEAR_STR"-"RUBY_RELEASE_MONTH_STR"-"RUBY_RELEASE_DAY_STR
-#define RUBY_PATCHLEVEL 85
+#define RUBY_PATCHLEVEL 86
#include "ruby/version.h"
#include "ruby/internal/abi.h"