Handling exceptions raised in a Ruby thread
If you want any unhandled exception in any thread to cause the interpreter to exit, you need to set Thread::abort_on_exception= to true
. Unhandled exception cause the thread to stop running. If you don't set this variable to true, exception will only be raised when you call Thread#join
or Thread#value
for the thread. If set to true it will be raised when it occurs and will propagate to the main thread.
Thread.abort_on_exception=true # add thisdef foo(n) puts " for #{n}" sleep n raise "after #{n}"endbegin threads = [] [15, 5, 20, 3].each do |i| threads << Thread.new do foo(i) end end threads.each(&:join)rescue Exception => e puts "EXCEPTION: #{e.inspect}" puts "MESSAGE: #{e.message}"end
Output:
for 5 for 20 for 3 for 15EXCEPTION: #<RuntimeError: after 3>MESSAGE: after 3
Note: but if you want any particular thread instance to raise exception this way there are similar abort_on_exception= Thread instance method:
t = Thread.new { # do something and raise exception}t.abort_on_exception = true
Thread.class_eval do alias_method :initialize_without_exception_bubbling, :initialize def initialize(*args, &block) initialize_without_exception_bubbling(*args) { begin block.call rescue Exception => e Thread.main.raise e end } endend
Postponed exceptions processing (Inspired by @Jason Ling)
class SafeThread < Thread def initialize(*args, &block) super(*args) do begin block.call rescue Exception => e @exception = e end end end def join raise_postponed_exception super raise_postponed_exception end def raise_postponed_exception Thread.current.raise @exception if @exception endendputs :startbegin thread = SafeThread.new do raise 'error from sub-thread' end puts 'do something heavy before joining other thread' sleep 1 thread.joinrescue Exception => e puts "Caught: #{e}"endputs 'proper end'