Project

General

Profile

Actions

Bug #12741

closed

Timeout with specified exception class sets cause on error if timeout block is handling an exception when timeout occurs

Added by rockfx01 (Tim Mertens) over 7 years ago. Updated about 7 years ago.

Status:
Closed
Assignee:
-
Target version:
-
ruby -v:
ruby 2.3.0p0 (2015-12-25 revision 53290) [x86_64-darwin14]
[ruby-core:77222]

Description

If Timeout.timeout is called with an exception class explicitly specified in the second argument, then if a timeout does occur while the underlying code wrapped by the Timeout block is handling an exception, that exception will bubble up to the error raised by the Timeout block and returned as the cause of the Timeout error, when in fact the two are not related.

For example, given the following method:

require 'timeout'
OuterError = Class.new(StandardError)
InnerError = Class.new(StandardError)

def timeout_error_includes_cause?
  begin
    Timeout.timeout 0.1, OuterError do
      loop do
        begin
          raise InnerError, 'An Error'
        rescue
          sleep 0.01
        end
      end
    end
  rescue => f
    if f.cause
      puts f.cause.class
      puts f.cause.message
      puts f.backtrace.join "\n"
    end
    !!f.cause
  end
end

Then when we call this method, we will see that >99% of the time, the error raised by the Timeout block will include the StandardError raised by the inner block as the cause of the OuterError, because the inner block is still in the process of handling the exception:

2.3.0 :025 > timeout_error_includes_cause?
InnerError
An Error
(irb):12:in `sleep'
(irb):12:in `rescue in block (2 levels) in timeout_error_includes_cause?'
(irb):9:in `block (2 levels) in timeout_error_includes_cause?'
(irb):8:in `loop'
(irb):8:in `block in timeout_error_includes_cause?'
~/.rvm/rubies/ruby-2.3.0/lib/ruby/2.3.0/timeout.rb:101:in `timeout'
(irb):7:in `timeout_error_includes_cause?'
(irb):25:in `irb_binding'
~/.rvm/rubies/ruby-2.3.0/lib/ruby/2.3.0/irb/workspace.rb:87:in `eval'
~/.rvm/rubies/ruby-2.3.0/lib/ruby/2.3.0/irb/workspace.rb:87:in `evaluate'
~/.rvm/rubies/ruby-2.3.0/lib/ruby/2.3.0/irb/context.rb:380:in `evaluate'
~/.rvm/rubies/ruby-2.3.0/lib/ruby/2.3.0/irb.rb:489:in `block (2 levels) in eval_input'
~/.rvm/rubies/ruby-2.3.0/lib/ruby/2.3.0/irb.rb:623:in `signal_status'
~/.rvm/rubies/ruby-2.3.0/lib/ruby/2.3.0/irb.rb:486:in `block in eval_input'
~/.rvm/rubies/ruby-2.3.0/lib/ruby/2.3.0/irb/ruby-lex.rb:246:in `block (2 levels) in each_top_level_statement'
~/.rvm/rubies/ruby-2.3.0/lib/ruby/2.3.0/irb/ruby-lex.rb:232:in `loop'
~/.rvm/rubies/ruby-2.3.0/lib/ruby/2.3.0/irb/ruby-lex.rb:232:in `block in each_top_level_statement'
~/.rvm/rubies/ruby-2.3.0/lib/ruby/2.3.0/irb/ruby-lex.rb:231:in `catch'
~/.rvm/rubies/ruby-2.3.0/lib/ruby/2.3.0/irb/ruby-lex.rb:231:in `each_top_level_statement'
~/.rvm/rubies/ruby-2.3.0/lib/ruby/2.3.0/irb.rb:485:in `eval_input'
~/.rvm/rubies/ruby-2.3.0/lib/ruby/2.3.0/irb.rb:395:in `block in start'
~/.rvm/rubies/ruby-2.3.0/lib/ruby/2.3.0/irb.rb:394:in `catch'
~/.rvm/rubies/ruby-2.3.0/lib/ruby/2.3.0/irb.rb:394:in `start'
~/.rvm/rubies/ruby-2.3.0/bin/irb:11:in `<main>'
 => true 

If we remove OuterError from the Timeout.timeout argument list, then the resulting Timeout::Error will never include the nested exception as a cause of the error raised by Timeout.

This has been tested and confirmed in the following ruby versions:

  • ruby 2.2.4p230 (2015-12-16 revision 53155) [x86_64-darwin15]
  • ruby 2.3.0p0 (2015-12-25 revision 53290) [x86_64-darwin14]
Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0