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?

···

--
Iñaki Baz Castillo
<ibc@aliax.net>

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

···

2012/6/7 Iñaki Baz Castillo <ibc@aliax.net>:

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

--
Iñaki Baz Castillo
<ibc@aliax.net>

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

···

On Thu, Jun 7, 2012 at 4:12 AM, Iñaki Baz Castillo <ibc@aliax.net> wrote:

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

--
Tony Arcieri

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.

···

On Jun 7, 2012, at 5:22 AM, Iñaki Baz Castillo <ibc@aliax.net> wrote:

2012/6/7 Iñaki Baz Castillo <ibc@aliax.net>:

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

--
Iñaki Baz Castillo
<ibc@aliax.net>

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:

···

2012/6/7 Iñaki Baz Castillo <ibc@aliax.net>:

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

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

--
Iñaki Baz Castillo
<ibc@aliax.net>

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.

···

2012/6/13 Tony Arcieri <tony.arcieri@gmail.com>:

On Thu, Jun 7, 2012 at 4:12 AM, Iñaki Baz Castillo <ibc@aliax.net> 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?

--
Iñaki Baz Castillo
<ibc@aliax.net>

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 Jam <jam@jamandbees.net>:

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.

--
Iñaki Baz Castillo
<ibc@aliax.net>

In order to re-check this possible "issue", I've reported it in Ruby tracker:

···

2012/6/7 Iñaki Baz Castillo <ibc@aliax.net>:

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

--
Iñaki Baz Castillo
<ibc@aliax.net>

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

···

On Tue, Jun 12, 2012 at 3:29 PM, Iñaki Baz Castillo <ibc@aliax.net> wrote:

2012/6/13 Tony Arcieri <tony.arcieri@gmail.com>:
> On Thu, Jun 7, 2012 at 4:12 AM, Iñaki Baz Castillo <ibc@aliax.net> > 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.

--
Iñaki Baz Castillo
<ibc@aliax.net>

--
Tony Arcieri

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 Castillo <ibc@aliax.net>:

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

In order to re-check this possible "issue", I've reported it in Ruby tracker:

Bug #6575: Thread#kill sets rb_errinfo() to Fixnum 8 after rb_protect(function, data, &error_tag) - Ruby master - Ruby Issue Tracking System

--
Iñaki Baz Castillo
<ibc@aliax.net>

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:

···

2012/6/13 Tony Arcieri <tony.arcieri@gmail.com>:

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

--
Iñaki Baz Castillo
<ibc@aliax.net>

I've found something in ruby sources:

thread.c:

···

2012/6/12 Iñaki Baz Castillo <ibc@aliax.net>:

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.

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

--
Iñaki Baz Castillo
<ibc@aliax.net>

Here an explanation of this "issue":

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

···

2012/6/13 Iñaki Baz Castillo <ibc@aliax.net>:

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

--
Iñaki Baz Castillo
<ibc@aliax.net>