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 #1

Updated by nobu (Nobuyoshi Nakada) over 7 years ago

  • Status changed from Open to Closed

Applied in changeset r56125.


thread.c: set cause by Thread#raise

  • thread.c (rb_threadptr_raise): set cause from the called thread,
    but not from the thread to be interrupted.
    [ruby-core:77222] [Bug #12741]
Actions #2

Updated by nagachika (Tomoyuki Chikanaga) about 7 years ago

  • Backport changed from 2.1: UNKNOWN, 2.2: UNKNOWN, 2.3: UNKNOWN to 2.1: UNKNOWN, 2.2: REQUIRED, 2.3: REQUIRED

Updated by nagachika (Tomoyuki Chikanaga) about 7 years ago

  • Backport changed from 2.1: UNKNOWN, 2.2: REQUIRED, 2.3: REQUIRED to 2.1: UNKNOWN, 2.2: REQUIRED, 2.3: DONE

ruby_2_3 r58026 merged revision(s) 56125,56150.

Updated by usa (Usaku NAKAMURA) about 7 years ago

  • Backport changed from 2.1: UNKNOWN, 2.2: REQUIRED, 2.3: DONE to 2.1: UNKNOWN, 2.2: DONE, 2.3: DONE

ruby_2_2 r58107 merged revision(s) 56125,56150.

Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0