[ruby-core:77222] [Ruby trunk Bug#12741] Timeout with specified exception class sets cause on error if timeout block is handling an exception when timeout occurs

From: tim@...
Date: 2016-09-08 20:23:33 UTC
List: ruby-core #77222
Issue #12741 has been reported by Tim Mertens.

----------------------------------------
Bug #12741: Timeout with specified exception class sets cause on error if timeout block is handling an exception when timeout occurs
https://bugs.ruby-lang.org/issues/12741

* Author: Tim Mertens
* Status: Open
* Priority: Normal
* Assignee: 
* ruby -v: ruby 2.3.0p0 (2015-12-25 revision 53290) [x86_64-darwin14]
* Backport: 2.1: UNKNOWN, 2.2: UNKNOWN, 2.3: UNKNOWN
----------------------------------------
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:


~~~ ruby
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]



-- 
https://bugs.ruby-lang.org/

Unsubscribe: <mailto:ruby-core-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>

In This Thread

Prev Next