Thread#kill is not rescued by "rescue Exception"

Hi, let me show this easy code:

def sleep_and_rescue
begin
sleep

Rescue ANY kind of exception.

rescue Exception => e
puts “exception rescued: #{e.class}: #{e.message}”
ensure
puts “ensure code…”
end
end

t1 = Thread.new { sleep_and_rescue() }
t1.raise “DIE”

=>
exception rescued: RuntimeError: DIE
ensure code…

(OK, what I expected)

t2 = Thread.new { sleep_and_rescue() }
t2.kill

=>
#<Thread:0x000000010743c8 aborting>
ensure code…

(opss, rescue block not executed…, the same if I set
t2.abort_on_exception=true/false)

So, it means that the signal send by Thread#kill is not
“rescue–able”, am I right?

2012/6/7 Iñaki Baz C. [email protected]:

So, it means that the signal send by Thread#kill is not
“rescue–able”, am I right?

Also, I’ve realized about the following in C code:

int exception_tag;
VALUE ret;

ret = rb_protect(function, Qnil, &exception_tag);

If while function() is being executed (which invokes Ruby land code)
current thread is killed with Thread#kill, then rb_protect() exits
with the following ANNOYING data:

exception_tag => int 8
ret => VALUE FIXNUM 8

If after that I do:

VALUE exception = rb_errinfo();

Then I get VALUE FIXNUM 8. Yes, rb_errinfo() returns FIXNUM 8 !!!

On Jun 7, 2012, at 5:22 AM, Iñaki Baz C. [email protected] wrote:

VALUE exception = rb_errinfo();

Then I get VALUE FIXNUM 8. Yes, rb_errinfo() returns FIXNUM 8 !!!


Iñaki Baz C.
[email protected]

As far as rescuing kill, you could open the Thread class and
reimplementing kill to throw an exception. There’s a piece of me that
thinks that’s a really bad idea, though.

2012/6/7 Jam [email protected]:

As far as rescuing kill, you could open the Thread class and reimplementing kill
to throw an exception. There’s a piece of me that thinks that’s a really bad idea,
though.

Thanks, but I don’t want to redefine the Thread class but just be
ready in case the user of my library calls to Thread#kill in his code.

2012/6/7 Iñaki Baz C. [email protected]:

with the following ANNOYING data:

exception_tag => int 8
ret => VALUE FIXNUM 8

If after that I do:

VALUE exception = rb_errinfo();

Then I get VALUE FIXNUM 8. Yes, rb_errinfo() returns FIXNUM 8 !!!

After more tests, this is my conclusion and what I will implement in my
C code:

static
VALUE execute_function_with_glv_and_rb_protect(void* function)
{
int error_tag = 0;
VALUE ret;

ret = rb_protect(function, Qnil, &error_tag);

/*

  • If an exception occurs while in function() it can be due:
    • An Exception (including SystemExit), this is “rescue-able” via
      “rescue Exception”
  • and will run the “ensure” code if present. In this case
    rb_errinfo() gets the
  • exact Exception object.
    • A Thread#kill. This is NOT “rescue-able” via “rescue Exception”
      but it WILL run
  • the “ensure” code if present. In this case rb_errinfo() returns
    FIXNUM 8 (it maybe
  • different, no idea).
  • So, check the class of the object returned by rb_errinfo(). If
    it’s an Exception then
  • store it, release the loop and raise it. Otherwise (Thread#kill)
    then don’t store the
  • exception returned by rb_errinfo() and just release the loop.
    Ruby will do the rest.
    */

Hope I’m right.

2012/6/7 Iñaki Baz C. [email protected]:

    • A Thread#kill. This is NOT “rescue-able” via “rescue Exception”
      but it WILL run
  • the “ensure” code if present. In this case rb_errinfo() returns
    FIXNUM 8 (it maybe
  • different, no idea).

In order to re-check this possible “issue”, I’ve reported it in Ruby
tracker:

Hi, could somebody please comment on my question? Summarizing:

int error_tag;
ret = rb_protect(function, data, &error_tag);

// While function() is being executed in Ruby land,
// its thread is killed by other thread via Thread.kill.

// If now I inspect rb_errinfo() it returns Fixnum 8, and
// error_tag it’s set to integer 8.

I’m coding my application assuming that, in case rb_errinfo() returns
a Fixnum (instead of a Exception object) then I must assume that its
thread has been killed via Thread#kill. Could somebody confirm whether
this assumption is correct or not?

NOTE: Ruby 1.9.3-p0 MRI.

Thanks a lot.

2012/6/11 Iñaki Baz C. [email protected]:

On Thu, Jun 7, 2012 at 4:12 AM, Iaki Baz C. [email protected] wrote:

So, it means that the signal send by Thread#kill is not
“rescue–able”, am I right?

I think that’s as it should be. If you want the same thing, but
“rescue-able”, why not Thread#raise?

2012/6/12 Iñaki Baz C. [email protected]:

// error_tag it’s set to integer 8.

I’m coding my application assuming that, in case rb_errinfo() returns
a Fixnum (instead of a Exception object) then I must assume that its
thread has been killed via Thread#kill. Could somebody confirm whether
this assumption is correct or not?

NOTE: Ruby 1.9.3-p0 MRI.

I’ve found something in ruby sources:

thread.c:

static void
rb_threadptr_execute_interrupts_common(rb_thread_t *th)
{
[…]

/* exception from another thread */
if (th->thrown_errinfo) {
VALUE err = th->thrown_errinfo;
th->thrown_errinfo = 0;
thread_debug(“rb_thread_execute_interrupts: %“PRIdVALUE”\n”, err);

if (err == eKillSignal || err == eTerminateSignal) {
  th->errinfo = INT2FIX(TAG_FATAL);
  TH_JUMP_TAG(th, TAG_FATAL);

and in eval_intern.h:

RUBY_TAG_FATAL = 0x8,
#define TAG_FATAL RUBY_TAG_FATAL

So, there is my Fixnum 8 :slight_smile:

2012/6/13 Tony A. [email protected]:

On Thu, Jun 7, 2012 at 4:12 AM, Iñaki Baz C. [email protected] wrote:

So, it means that the signal send by Thread#kill is not
“rescue–able”, am I right?

I think that’s as it should be. If you want the same thing, but
“rescue-able”, why not Thread#raise?

Hi Tony, I don’t want to use Thread#kill nor Thread#raise. It’s just
that I want my application to be ready if the user decides to kill the
thread with Thread#kill.

Regards.

Seems good!

I’ve exposed Thread#kill in my library (Celluloid) as a sort of last
resort.

As for Thread#raise, I think if you reraise the exception at the end of
your error handler after rescuing Exception when it isn’t a
StandardError,
you should be good

2012/6/13 Iñaki Baz C. [email protected]:

The only “problem” is that, as said in my other mails, when the thread
is killed with Thread#kill, rb_errinfo() returns Fixnum(8 =
RUBY_TAG_FATAL) instead of an Exception object, so trying to raise a
Fixnum is not a good idea :slight_smile:

And… it seems that all of this is due a bug in Ruby:

http://bugs.ruby-lang.org/projects/ruby-trunk/repository/revisions/35622
Bug #5993: Thread.new{ Fiber.new { Thread.exit }.resume }.join で例外 - Ruby master - Ruby Issue Tracking System

Here an explanation of this “issue”:

http://bugs.ruby-lang.org/projects/ruby-trunk/repository/revisions/35625

2012/6/13 Tony A. [email protected]:

Seems good!

I’ve exposed Thread#kill in my library (Celluloid) as a sort of last resort.

As for Thread#raise, I think if you reraise the exception at the end of your
error handler after rescuing Exception when it isn’t a StandardError, you
should be good

Yes, during the libuv loop execution I execute all the Ruby callbacks
with rb_protect(). If an exception/error occurs then I pass it (the
exception object retrieved via rb_errinfo() function) to the “error
handler” of my library which, in case it’s a StandardError it would
call the user provided block (optional) to react on errors. Otherwise
(not a standard error) my library properly releases the libuv loop and
re-raises the captured exception.

The only “problem” is that, as said in my other mails, when the thread
is killed with Thread#kill, rb_errinfo() returns Fixnum(8 =
RUBY_TAG_FATAL) instead of an Exception object, so trying to raise a
Fixnum is not a good idea :slight_smile:

And… it seems that all of this is due a bug in Ruby:

http://bugs.ruby-lang.org/projects/ruby-trunk/repository/revisions/35622

:slight_smile: