unexpected behaviour of ruby threading code snippet - ruby

I am trying to learn threading in ruby, i am executing following code:
Thread.abort_on_exception = true
threads = 5.times.map do |i|
Thread.new(i) do |j|
raise "Boom!" if j == 1
print "#{j}\n"
end
end
i=0
loop do
i+=1
puts "Waiting!!" if i == 10000
break if i == 10**4
end
threads.each do |th|
begin
th.join
rescue RuntimeError => e
puts e
end
end
puts "Done!!"
Sometimes it run perfectly without exception and shows output like:
Waiting!!
0
2
3
4
Boom!
Boom!
Done!!
and sometimes it quits with exception and shows output like:
0
2
3
4
threading.rb:5:in `block (2 levels) in <main>': Boom! (RuntimeError)
Now my questions are:
Why? it quits with RuntimeError while i have already rescued it.
How could there be two Boom! in output.
Development environment:
Windows 7 (x64)
Ruby 2.1.5

The exception is happening in a thread before the main thread has reached the block with the Thread#join call; specifically, within your loop. The problem is that, with exceptions bubbling up to the main thread, it can literally happen on any line of code; thus, you need to encapsulate error handling within a thread as well. Perhaps it can return an error value, but exceptions are fundamentally broken when using this concurrency model.
(To prove this to yourself, try setting $DEBUG = true at the top of your file; this will show you where the exceptions really occur.)

Related

How do I prevent two exception stacks when catching and re-raising Ruby exceptions?

In a larger project, I am catching specific exceptions, adding some details to the message and then re-raising the same exception. Here is the basic idea of what I'm doing.
#!/usr/bin/env ruby
begin
puts 12 / 0
rescue ZeroDivisionError => e
raise e.exception("new message #{e.message}")
end
When I execute temp.rb from the command line, two exceptions are printed:
% ./temp.rb
./temp.rb:4:in `/': new message divided by 0 (ZeroDivisionError)
from ./temp.rb:4:in `<main>'
./temp.rb:4:in `/': divided by 0 (ZeroDivisionError)
from ./temp.rb:4:in `<main>'
I'm assuming that there is a stack or a list of exceptions somewhere and the exit process of Ruby prints out the entire list or stack of exceptions. I can see great benefits to this. But in my case, I'd like to get rid of the original exception from the list or stack. Is there a way to do this?
The "second" (original) exception is the cause of the rescued exception which is references by your new exception as it is created with Exception#exception.
Starting with Ruby 3.1, irb prints the details of the cause in addition to the actual exception to aid in debugging. Previous Ruby versions have ignored the cause here.
Thus, you can use other means to handle your exceptions rather than using Ruby's default handler of printing the details of an otherwise unhandled exception, e.g. by adding an explicit exception handler. Alternatively, you can also explicitly set the cause as you are creating your new exception:
begin
puts 12 / 0
rescue ZeroDivisionError => e
raise e.class, "new message #{e.message}", cause: nil
end
May be something like this to extend Exception with custom method
Exception.class_eval do
def add_message(msg)
mod =
Module.new do
define_method :to_s do
"#{super()} #{msg}"
end
end
extend mod
end
end
And then
begin
puts 12 / 0
rescue ZeroDivisionError => e
raise e.add_message("(even don't try it)")
end

Rescue simultaneous errors raised inside threads

With the following script
threads = [
Thread.new { Thread.current.abort_on_exception = true; raise 'err' },
Thread.new { Thread.current.abort_on_exception = true; raise 'err' },
]
begin
threads.each(&:join)
rescue RuntimeError
puts "Got Error"
end
Half the time I get the expected "Got Error" with exit 0 and the other half I get test.rb:3:in block in <main>': err (RuntimeError).
Shouldn't rescue be able to handle this? If not what might be some alternative solutions for two threads raising an error simultaneously?
I've considered not using abort_on_exception = true but the problem is that if the first thread has, say sleep(10) before the raise, the second thread, which errors immediately, won't get caught until 10 seconds is up (due to the order of the threads array).
Ruby MRI version:
ruby 2.4.0p0 (2016-12-24 revision 57164) [x86_64-darwin15]
Any ideas would be greatly appreciated. Thanks!
Update
jruby-9.1.6.0 doesn't seem to have this problem. Possibly because the inherent thread safeness of it. It always prints Got Error without any exceptions. Unfortunately, JRuby is not on option for us.
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
After looking at #ndn's idea of wrapping each thread in it's own rescue. It looks like this solves the problem. Here is his modified example that doesn't block execution on join.
#threads = []
def execute_safely_concurrently(&work)
begin
#threads << Thread.new(&work)
rescue RuntimeError => e
puts "Child Thread Rescue: #{e}"
end
end
execute_safely_concurrently do
Thread.current.abort_on_exception = true
sleep(3)
raise 'Handle me, bitte 1!'
end
execute_safely_concurrently do
Thread.current.abort_on_exception = true
raise 'Handle me, bitte 2!'
end
begin
#threads.each(&:join)
rescue RuntimeError => e
puts "Main Thread Rescue: #{e}"
end

Ruby error reporting in multithreaded environment [duplicate]

This question already has answers here:
Handling exceptions raised in a Ruby thread
(5 answers)
Closed 8 years ago.
I am attempting to write a multithreaded program in Ruby. Errors that occur inside the thread don't report an error message to stdout. I have pared my code down to a minimal program to demonstrate.
In the first example, I create an error in the main block with a meaningless statement:
puts "hello"
blah
my_thread = Thread.new do
end
sleep 1
In this case the error is reported normally:
$ ruby bin/test.rb
$ hello
$ bin/test.rb:2:in <main>': undefined local variable or methodblah' for main:Object (NameError)
However, if I put the error-causing call inside the thread:
my_thread = Thread.new do
puts "hello"
blah
end
sleep 1
no error is reported:
$ ruby bin/test.rb
hello
If I move the puts statement to after the error-causing call it never gets executed, so the error must be halting execution (of the thread).
I tried using exception handling as well - neither of the following echo an error to stdout:
my_thread = Thread.new do
begin
puts "hello"
blah
rescue StandardError => msg
puts "Error: " + msg
end
end
sleep 1
or:
begin
my_thread = Thread.new do
puts "hello"
blah
end
rescue StandardError => msg
puts "Error: " + msg
end
sleep 1
I've tried including the better_errors gem in the hope that it 'just works' like it does in Rails projects that have it, my results are no different.
I'm also trying to debug the program in my IDE (RubyMine) but breakpoints inside of the threads never get hit.
If you join the thread, you will get the error message.
my_thread = Thread.new do
puts 'hello'
blah
end
my_thread.join # <-----
output:
hello
t.rb:3:in `block in <main>': undefined local variable or method `blah' for main:Object (NameError)
Alternatively, you can use Thread#value (it calls Thread#join).
According to the Thread#join documentation:
...
Any threads not joined will be killed when the main program exits. If
thr had previously raised an exception and the abort_on_exception and
$DEBUG flags are not set (so the exception has not yet been processed)
it will be processed at this time.

Variables not recognized within Rescue in Ruby

I have the following code:
rescue Timeout::Error, StandardError => e
puts "Caught exception: #{e.message}".red
log.puts("#{e.backtrace}")
email_ids_all.each do |email_delete|
call= "/api/v2/emails/#{email_delete}/"
......
Before this rescue piece I have defined log and email_ids_all. However, neither of these are recognized within the ruby script. If i do this:
rescue Timeout::Error, StandardError => e
File.open(rescuelogfile, 'w') do |log| #setup log to write response codes.
puts "Caught exception: #{e.message}".red
log.puts("#{e.backtrace}")
email_ids_all.each do |email_delete|
call= "/api/v2/emails/#{email_delete}/"
....
log works fine, which makes sense. It would take a lot of writing to redefine the email_ids_all array and other variables contained inside my rescue block.
Is there anyway to allow variables to be recognized inside the rescue? Basically my code is laid out like this:
begin
#some code
rescue
#above code
end
I am using ruby 1.9.3.
EDIT----
log starts right after my begin statement :
begin
File.open(logfile, 'w') do |log| #setup log to write response codes.
log.puts works throughout the entire code except when an error is thrown, and then it runs the rescue script where log is not available.
The same goes for email_ids_all. There is an API call that generates about 10,000 emails and each of them is added to the array email_ids_all. The script is receiving an error about halfway through generating these emails, and so I need the rescue script to delete all email ids in the email_ids_all array. But for whatever reason, I get the following error:
FS_Test_Env.rb:762:in `block in <main>': undefined local variable or method `email_ids_all' for main:Object (NameError)
from FS_Test_Env.rb:759:in `open'
from FS_Test_Env.rb:759:in `rescue in <main>'
from FS_Test_Env.rb:7:in `<main>'
Any thoughts?
The way you put it, it should work, for example:
irb(main):001:0> begin
irb(main):002:1* x = 1
irb(main):003:1> x / 0
irb(main):004:1> rescue Exception => e
irb(main):005:1> p x
irb(main):006:1> end
1
=> 1
So it looks like the exception is thrown before your variables are defined.
The scope of the block parameter log is limited to that block. This is the whole point of the open with block.
Maybe you want to do:
begin
log = File.open('logfile', 'w')
...
rescue
...
ensure
log.close
end
Note that this does not cover errors when opening the logfile.
Regarding email_ids_all, I guess (!) you have the exception in a statement like:
email_ids_all = ... a long and complex calculation which raises an exception
If yes, the problem is that the assignment happens only after the whole right-hand side is calculated. The var email_ids_all is not yet created when the exception happens.
In order to access the elements created before the exception, you have to keep track of them, e.g.
begin
email_ids = []
10000.times do
email_ids << ... # create element eventually raising an exception
end
rescue
... # treat the already created elements
end

How to make Ruby capture the syntax error in threads

I am trying to code up a two-threads client using ruby, one thread reads the data from a socket and print it out, the other thread reads local data and send it to remote server. The problem I found is that it looks like Ruby cannot capture errors within a thread, here is an example:
#! /usr/bin/ruby
Thread.new {
loop {
$stdout.puts "hi"
abc.puts ef
sleep 1
}
}
loop {
sleep 1
}
Obviously, outside the thread if I type abc.puts ef the code will never run since Ruby will report "undefined variable abc". However if it is within a thread, there is no error report. My question is, how to let Ruby capture errors like this? Or at least, report something is wrong within a thread?
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>'
For syntax error catching, rescue must use explict Exception class
(without this, rescue catch only StandardError) :
Thread.new {
begin
abc.puts ef
rescue Exception => e
puts "error #{e}"
end
}
see Why can't `rescue` catch exception classes other than `StandardError` by default?
Ok, one possible solution is surround the thread lambda with begin rescue end block:
Thread.new {
begin
abc.puts ef
rescue
puts error
end
}

Resources