From 78a8a371ec75802f0f31b45a941cd8530d5f6529 Mon Sep 17 00:00:00 2001 From: Jeremy Evans Date: Tue, 20 Apr 2021 10:34:11 -0700 Subject: [PATCH 1/3] Make Timeout::Error#exception with multiple arguments not ignore arguments This makes: raise(Timeout::Error.new("hello"), "world") raise a TimeoutError instance with "world" as the message instead of "hello", for consistency with other Ruby exception classes. This required some internal changes to keep the tests passing. Fixes [Bug #17812] --- lib/timeout.rb | 9 ++++++--- test/test_timeout.rb | 8 ++++++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/lib/timeout.rb b/lib/timeout.rb index 11db4be..572e90c 100644 --- a/lib/timeout.rb +++ b/lib/timeout.rb @@ -32,7 +32,9 @@ class Error < RuntimeError def self.catch(*args) exc = new(*args) exc.instance_variable_set(:@thread, Thread.current) - ::Kernel.catch(exc) {yield exc} + catch_value = Object.new + exc.instance_variable_set(:@catch_value, catch_value) + ::Kernel.catch(catch_value) {yield exc} end def exception(*) @@ -40,11 +42,11 @@ def exception(*) if self.thread == Thread.current bt = caller begin - throw(self, bt) + throw(@catch_value, bt) rescue UncaughtThrowError end end - self + super end end @@ -115,6 +117,7 @@ def timeout(sec, klass = nil, message = nil, &block) #:yield: +sec+ begin bl.call(klass) rescue klass => e + message = e.message bt = e.backtrace end else diff --git a/test/test_timeout.rb b/test/test_timeout.rb index c57d90c..71607ed 100644 --- a/test/test_timeout.rb +++ b/test/test_timeout.rb @@ -80,6 +80,14 @@ def test_exit_exception end end + def test_raise_with_message + bug17812 = '[ruby-core:103502] [Bug #17812]: Timeout::Error doesn\'t let two-argument raise() set a new message' + exc = Timeout::Error.new('foo') + assert_raise_with_message(Timeout::Error, 'bar', bug17812) do + raise exc, 'bar' + end + end + def test_enumerator_next bug9380 = '[ruby-dev:47872] [Bug #9380]: timeout in Enumerator#next' e = (o=Object.new).to_enum From d173bb072b4f1c15b3615ed05d7c02036dce4032 Mon Sep 17 00:00:00 2001 From: Jeremy Evans Date: Mon, 3 May 2021 08:31:59 -0700 Subject: [PATCH 2/3] Avoid unnecessary object allocation Idea from nobu. --- lib/timeout.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/timeout.rb b/lib/timeout.rb index 572e90c..ee24349 100644 --- a/lib/timeout.rb +++ b/lib/timeout.rb @@ -32,9 +32,8 @@ class Error < RuntimeError def self.catch(*args) exc = new(*args) exc.instance_variable_set(:@thread, Thread.current) - catch_value = Object.new - exc.instance_variable_set(:@catch_value, catch_value) - ::Kernel.catch(catch_value) {yield exc} + exc.instance_variable_set(:@catch_value, exc) + ::Kernel.catch(exc) {yield exc} end def exception(*) From 25de5e4878b8c68ddd4c1fc1025a7bcfc053f642 Mon Sep 17 00:00:00 2001 From: Jeremy Evans Date: Mon, 3 May 2021 08:38:54 -0700 Subject: [PATCH 3/3] Only run timeout_after hook on fiber scheduler if scheduler exists --- lib/timeout.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/timeout.rb b/lib/timeout.rb index ee24349..e7b11c0 100644 --- a/lib/timeout.rb +++ b/lib/timeout.rb @@ -85,7 +85,7 @@ def timeout(sec, klass = nil, message = nil, &block) #:yield: +sec+ message ||= "execution expired".freeze - if (scheduler = Fiber.current_scheduler)&.respond_to?(:timeout_after) + if Fiber.respond_to?(:current_scheduler) && (scheduler = Fiber.current_scheduler)&.respond_to?(:timeout_after) return scheduler.timeout_after(sec, klass || Error, message, &block) end