How to Get Error Messages from Ruby Threads

How to get error messages from ruby threads

Set the Thread class' abort_on_exception flag to true.

Alternately, wrap the thread body in a throw/catch block and dump the exception in the catch.

Ruby threads: how to catch an exception without calling .join?

I figured out how to do it - I just need to wrap insides of my thread into a rescue block like that:

Thread.abort_on_exception = true

a = Thread.new do
begin #new
errory_code
rescue Exception => detail #new
puts detail.message #new
end #new
end

sleep 1 #so that thread has enough time to execute

Which is a horribly obvious solution, - but, since it took me so long to think of it, will hopefully help somebody.

How to make Ruby capture the syntax error in threads

Use Thread::abort_on_exception=:

According to Thread - Exception Handling:

Any thread can raise an exception using the raise instance method,
which operates similarly to Kernel#raise.

However, it's important to note that an exception that occurs in any
thread except the main thread depends on abort_on_exception. This
option is false by default, meaning that any unhandled exception will
cause the thread to terminate silently when waited on by either join
or value. You can change this default by either abort_on_exception=
true or setting $DEBUG to true.

...

Thread::abort_on_exception = true
Thread.new {
loop {
$stdout.puts "hi"
abc.puts ef
sleep 1
}
}

loop {
sleep 1
}

=>

hi
t.rb:5:in `block (2 levels) in <main>': undefined local variable or method `abc' for main:Object (NameError)
from t.rb:3:in `loop'
from t.rb:3:in `block in <main>'

How do I catch an error from a thread and then re-throw that error when all the threads have completed?

To answer your question - no actual way as the library explicitly silences exceptions and there is no configuration for it.

A possible workaround would be to capture exceptions manually:

error = nil
pool = Concurrent::FixedThreadPool.new(1)
numbers.each do |number|
pool.post do
begin
some_dangerous_action(number)
rescue Exception => e
error = e
raise # still let the gem do its thing
end
end
end

pool.shutdown
pool.wait_for_termination

raise error if error

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

Ruby: rescue doesn't rescue from thread

There are a few problems with what you have written that I think are clouding the issue for you.

  1. You shouldn’t set Thread.abort_on_exception = true unless you really want your program to abort when an exception occurs in a secondary thread. Typically, you’d only want this set to true for debugging. If set to false, any exception raised in a thread will cause that thread to exit but will only be visible to the parent thread when you join with the child thread.

  2. In your code above, when you attempt to join with a thread, the variable a is out of scope. Thus you should get a NameError there too.

  3. Even if this were in scope, you are only joining with one of your threads.

You should find the following more predictable:

Thread.abort_on_exception = false

begin
threads = [1, 2].collect do |i|
Thread.new do
errory
end
end
threads.each { |thread| thread.join } # You might want to use a ThreadsWait here instead.
rescue Exception
puts 'error!'
end

Rescue simultaneous errors raised inside threads

Here are a few pieces of the puzzle here.


Firstly, the program only waits for the main thread to finish:

Thread.new { Thread.current.abort_on_exception = true; raise 'Oh, no!' }

puts 'Ready or not, here I come'

The above may or may not raise the error.


Secondly, if you join on a thread, an exception raised by that threads is re-raised by the joined thread from the #join method:

gollum = Thread.new { raise 'My precious!!!' }

begin
gollum.join
rescue => e
# Prints 'My precious!!!'
puts e.message
end

Now at that point, the execution is returned to the thread that joined. It is no longer joined to the thread that caused the error or any other thread. The reason it's not joined to other threads is because you can only join one thread at the time. threads.each(&:join) actually joins you to the first, when it ends - to the second and so on:

frodo = Thread.new { raise 'Oh, no, Frodo!' }
sam = Thread.new { raise 'Oh, no, Sam!' }

begin
[frodo, sam].each(&:join)
rescue => e
puts e.message
end

puts 'This is the end, my only friend, the end.'

The above prints

Oh, no, Frodo!

This is the end, my only friend, the end.


Now lets put it together:

frodo = Thread.new { Thread.current.abort_on_exception = true; raise 'Oh, no, Frodo!' }
sam = Thread.new { Thread.current.abort_on_exception = true; raise 'Oh, no, Sam!' }

begin
[frodo, sam].each(&:join)
rescue => e
puts e.message
end

puts 'This is the end, my only friend, the end.'

Many things can happen here. What is of importance is, if we manage to join (we don't get an error before that), the rescue will catch the exception in the main thread from whichever thread manages to raise it first and then continue after the rescue. After that, the main thread (and thus the program) may or may not finish before the other thread raises its exception.


Let examine some possible outputs:

ex.rb:1:in `block in ': Oh, no, Frodo! (RuntimeError)

Frodo raised his exception before we joined.

Oh, no, Sam!

This is the end, my only friend, the end.

After we joined, Sam was the first to raise an error. After we printed the error message in the main thread, we also printed the end. Then the main thread finished, before Frodo could raise his error.

Oh, no, Frodo!ex.rb:2:in `block in ': Oh, no, Sam! (RuntimeError)

We managed to join. Frodo was the first to raise, we rescued and printed. Sam raised before we could print the end.

Oh, no, Sam!

This is the end, my only friend, the end.ex.rb:1:in `block in ': Oh, no, Frodo! (RuntimeError)

(Very rarely) We managed to get to the rescue. Sam raised an error first and we printed it from the main thread. We printed the end. Just after the print, but before the main thread is terminated, Frodo managed to jab his error as well.


As for a possible solution, you just need as many rescues as there are threads that might raise. Note that I also put the thread creation in the safeguarded block to ensure we catch potential errors before joins as well:

def execute_safely_concurrently(number_of_threads, &work)
return if number_of_threads.zero?

begin
Thread.new(&work).join
rescue => e
puts e
end

execute_safely_concurrently(number_of_threads.pred, &work)
end

execute_safely_concurrently(2) do
Thread.current.abort_on_exception = true
raise 'Handle me, bitte!'
end

Rails 4 - threading error

You're probably spawning more threads than ActiveRecord's DB connection pool has connections. Ekkehard's answer is an excellent general description; so here's a simple example of how to limit your workers using Ruby's thread-safe Queue.

require 'thread'

queue = Queue.new
items.each { |i| queue << i } # Fill the queue

Array.new(5) do # Only 5 concurrent workers
Thread.new do
until queue.empty?
item = queue.pop
ActiveRecord::Base.connection_pool.with_connection do
# Work
end
end
end
end.each(&:join)

I chose 5 because that's the ConnectionPool's default, but you can certainly tune that to the max that still works, or populate another queue with the result to save later and run an arbitrary number of threads for the calculation.

The with_connection method grabs a connection, runs your block, then ensures the connection is released. It's necessary because of a bug in ActiveRecord where the connection doesn't always get released otherwise. Check out this blog post for some details.



Related Topics



Leave a reply



Submit