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.

···

--
Posted via http://www.ruby-forum.com/.

oliver wrote:

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?

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)

···

--
       vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407

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.

···

--
       vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407

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)

--
Posted via http://www.ruby-forum.com/\.

Oliver Peng 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.)

···

--
       vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407

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?

···

On 6/7/07, Oliver Peng <oliver.peng@skywave.com> 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.

--
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 Peng <oliver.peng@skywave.com> 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/

--
Posted via http://www.ruby-forum.com/\.

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

···

On 07.06.2007 22:03, Oliver Peng 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.