Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Handling exceptions raised in a Ruby thread

I am looking for a solution of classic problem of exception handling. Consider following piece of code:

def foo(n)   puts " for #{n}"   sleep n   raise "after #{n}" end  begin   threads = []   [5, 15, 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 

This code catches the exception after 5 seconds.

But if I change the array as [15, 5, 20, 3], above code catch the exception after 15 seconds. In short, it always catch the exception raised in first thread.

Any idea, why so. Why doesn't it catch the exception after 3 seconds each time? How do I catch the first raised exception by any thread?

like image 311
Akash Agrawal Avatar asked Feb 01 '12 11:02

Akash Agrawal


People also ask

How do you handle exceptions in Ruby?

Ruby also provides a separate class for an exception that is known as an Exception class which contains different types of methods. The code in which an exception is raised, is enclosed between the begin/end block, so you can use a rescue clause to handle this type of exception.

How do you raise exceptions in Ruby?

Ruby actually gives you the power to manually raise exceptions yourself by calling Kernel#raise. This allows you to choose what type of exception to raise and even set your own error message. If you do not specify what type of exception to raise, Ruby will default to RuntimeError (a subclass of StandardError ).

How do you handle exceptions in threads?

Exception handling in Thread : By default run() method doesn't throw any exception, so all checked exceptions inside the run method has to be caught and handled there only and for runtime exceptions we can use UncaughtExceptionHandler.

Does raising an error stop execution Ruby?

When you raise an exception in Ruby, the world stops and your program starts to shut down. If nothing stops the process, your program will eventually exit with an error message.


1 Answers

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 this  def foo(n)     puts " for #{n}"     sleep n     raise "after #{n}" end  begin     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 15 EXCEPTION: #<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 
like image 88
Aliaksei Kliuchnikau Avatar answered Sep 23 '22 16:09

Aliaksei Kliuchnikau