Raise problem in Thread

Define following class:
class MyThread < Thread
def initialize()
super{
puts “started”
sleep 1000
}
end

def test_raise()
raise StandardError.new()
puts “hello”
end
end

Open irb and run:
irb(main):082:0> thread = MyThread.new
started=> #<MyThread:0xb7cec234 run>
irb(main):093:0> thread.test_raise
hello
=> nil

I think the line “puts “hello”” should not be run after raising an
exception.
What is the reason of this problem?

Thanks.

oliver wrote:

raise StandardError.new()

I think the line “puts “hello”” should not be run after raising an
exception.
What is the reason of this problem?

The problem is that test_raise is executed not by the MyThread thread,
but by the main thread. The MyThread instance receives the exception and
dies (silently because abort_on_exception is off), and the main thread
continues executing the puts “hello”.

Keep in mind that the following contexts are different:

(1) self, the object that receives a message and performs the lookup
to determine which method handles the message

(2) Thread.current: the thread that the interpreter is currently running
to execute a method

Let’s look at the context when you call test_raise. Even though the
self is your MyThread instance, the current thread is still the main
thread.

If you want to send commands to a thread and have them executed by the
thread, you might want to set up a Queue that it reads.

Also, it’s useful to have the following when debugging threads:

Thread.abort_on_exception = true

(do this at the beginning of your program–you can also set this for
individual threads, but the global setting is nice for debugging)

Joel VanderWerf wrote:

If you want to send commands to a thread and have them executed by the
thread, you might want to set up a Queue that it reads.

A queue is also much safer than Thread.raise, as was discussed on
ruby-talk in the last few months, because raise can interrupt an ensure
clause and result in cleanup not happening.

Hi Joel:

Thank you for your reply.

One thing I am a little confused is that the main thread continues
executing the puts “hello”. From my point of view, puts “hello” is part
of method test_raise. So after the exception was raised and the MyThread
instance was dead, the method should stopping running and just return
nil to main thread. Why main thread need to continue running left part
of this method? This can cause some unexpecting problems.

Thanks.

Joel VanderWerf wrote:

oliver wrote:

raise StandardError.new()

I think the line “puts “hello”” should not be run after raising an
exception.
What is the reason of this problem?

The problem is that test_raise is executed not by the MyThread thread,
but by the main thread. The MyThread instance receives the exception and
dies (silently because abort_on_exception is off), and the main thread
continues executing the puts “hello”.

Keep in mind that the following contexts are different:

(1) self, the object that receives a message and performs the lookup
to determine which method handles the message

(2) Thread.current: the thread that the interpreter is currently running
to execute a method

Let’s look at the context when you call test_raise. Even though the
self is your MyThread instance, the current thread is still the main
thread.

If you want to send commands to a thread and have them executed by the
thread, you might want to set up a Queue that it reads.

Also, it’s useful to have the following when debugging threads:

Thread.abort_on_exception = true

(do this at the beginning of your program–you can also set this for
individual threads, but the global setting is nice for debugging)

Oliver P. wrote:

Hi Joel:

Thank you for your reply.

One thing I am a little confused is that the main thread continues
executing the puts “hello”. From my point of view, puts “hello” is part
of method test_raise. So after the exception was raised and the MyThread
instance was dead, the method should stopping running and just return
nil to main thread. Why main thread need to continue running left part
of this method? This can cause some unexpecting problems.

Even if a thread is dead, it still responds to methods. It’s still an
object, and all objects have a (private) #puts method that they inherit
from Kernel.

This is actually very useful behavior:

th = Thread.new do
1+2 # or some long computation
end

sleep 0.1
p th.alive? # ==> false
p th.value # ==> 3

The thread is dead, but you can retrieve the result. (If you call
Thread#value before the thread has finished, then it waits for it to
finish.)

On 6/7/07, Oliver P. [email protected] wrote:

Hi Joel:

Thank you for your reply.

One thing I am a little confused is that the main thread continues
executing the puts “hello”. From my point of view, puts “hello” is part
of method test_raise. So after the exception was raised and the MyThread
instance was dead, the method should stopping running and just return
nil to main thread. Why main thread need to continue running left part
of this method? This can cause some unexpecting problems.

As Joel explained, the puts is runs on the current thread, but the
exception is raised on the other thread. Just because some code is a
method of a thread class doesn’t mean that it automatically runs on
any particular thread.

Let’s try a little change to your code, change that puts “Hello” to
puts “I am #{self}, current thread is #{Thread.current}”.

Now in irb:
irb(main):013:0> thread = MyThread.new
started=> #<MyThread:0xb7b99a6c run>
irb(main):014:0>
thread.test_raise
I am #MyThread:0xb7b99a6c, current_thread is #Thread:0xb7e087d0
=> nil
irb(main):015:0> Thread.current
=> #<Thread:0xb7e087d0 run>

Does that help?


Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

Hi Rick:

Thank you for your reply.

So when we call raise method, we are calling the MyThread instance’s
raise method and it stop running of MyThread instance. I tried to use
Kernel.raise and Thread.current.raise. They all work fine and raise
exception in main thread.

Thanks again.

Oliver

Rick Denatale wrote:

On 6/7/07, Oliver P. [email protected] wrote:

Hi Joel:

Thank you for your reply.

One thing I am a little confused is that the main thread continues
executing the puts “hello”. From my point of view, puts “hello” is part
of method test_raise. So after the exception was raised and the MyThread
instance was dead, the method should stopping running and just return
nil to main thread. Why main thread need to continue running left part
of this method? This can cause some unexpecting problems.

As Joel explained, the puts is runs on the current thread, but the
exception is raised on the other thread. Just because some code is a
method of a thread class doesn’t mean that it automatically runs on
any particular thread.

Let’s try a little change to your code, change that puts “Hello” to
puts “I am #{self}, current thread is #{Thread.current}”.

Now in irb:
irb(main):013:0> thread = MyThread.new
started=> #<MyThread:0xb7b99a6c run>
irb(main):014:0>
thread.test_raise
I am #MyThread:0xb7b99a6c, current_thread is #Thread:0xb7e087d0
=> nil
irb(main):015:0> Thread.current
=> #<Thread:0xb7e087d0 run>

Does that help?


Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

On 07.06.2007 22:03, Oliver P. wrote:

So when we call raise method, we are calling the MyThread instance’s
raise method and it stop running of MyThread instance. I tried to use
Kernel.raise and Thread.current.raise. They all work fine and raise
exception in main thread.

Basically whenever you invoke “raise” somewhere in code it’s really
Kernel’s raise which delegates to Thread.current.raise. As you see,
#raise is also an instance method of Thread so your situation was
special because your class inherited Thread. That’s why the raise had a
slightly different effect, namely raising the exception in that thread
and not in the calling thread. Maybe this helps clarify:

irb(main):001:0> Thread.current.methods.grep /raise/
=> [“raise”]
irb(main):002:0> Thread.instance_methods.grep /raise/
=> [“raise”]
irb(main):003:0> method :raise
=> #<Method: Object(Kernel)#raise>
irb(main):004:0> Thread.current.method :raise
=> #<Method: Thread#raise>

It’s often confusing when first confronted with multithreading in OO
languages like Ruby and Java: Thread’s are instances (i.e. objects) like
all other objects but at the same time, there is a thread of execution
(i.e. stack, program counter etc.) associated with them. But this
relationship is not fixed: you can have Thread objects with no execution
thread associated. In Java this is between creation of a Thread
instance and invoking start() and after the thread terminated. In Ruby,
since threads are started immediately, it’s only after a thread
terminated.

Kind regards

robert