2012/5/29 Peter Z. [email protected]:
structure, rather the current way where nested Ruby calls map to nested
but also there is no sane way to synchronize their execution with main
avoid
signal/longjmp-caused interruptions for your code. I would also really
discourage
you from messing with signals: they’re already a big pile of crap, don’t add
even
more compelxity there. Longjmp is, too, but at least it’s synchronous.
Hi Peter, thanks a lot for your explanation. It’s clear and I think I
found the way to go:
My C exten runs a libuv (GitHub - joyent/libuv: Go to) C loop
without GVL and when an event occurs (data received, timer fires…)
it acquires the GVL and executes the user provider Ruby callback in
Ruby land. In this trip from C to Ruby it could occur that, when Ruby
takes the control, it decides to generate a SystemExit or whatever
interruption (due to a non-trapped signal, exit() command or
whatever). If I set a rescue/ensure after my Ruby blocking function
(which started the libuv loop) then libuv gets broken due the longjmp
and I cannot recover it.
So the solution has been to use rb_protect() when executing the Ruby
callback and catch any kind of exception:
static
VALUE
execute_function_with_glv_and_rb_protect(function_with_gvl_and_protect
function)
{
int exception_tag = 0;
VALUE ret;
ret = rb_protect(function, Qnil, &exception_tag);
// If an exception occurred then call to handle_exception() method.
if (exception_tag) {
VALUE exception = rb_errinfo();
// Dissable the current thread exception.
rb_set_errinfo(Qnil);
// Call AsyncEngine.handle_exception().
rb_funcall2(mAsyncEngine, method_handle_exception, 1, &exception);
return exception;
}
// Otherwise just return the VALUE returned by rb_protec() above.
else
return ret;
}
/*
- Executes the given function taking the GVL and using rb_protect().
*/
VALUE ae_execute_in_ruby_land(function_with_gvl_and_protect function)
{
AE_TRACE();
return
rb_thread_call_with_gvl(execute_function_with_glv_and_rb_protect,
function);
}
So when a libuv C callback is called, I call within it to
ae_execute_in_ruby_land(some_C_function). If an exception has occurred
during the trip to Ruby I call to my handle_exception() Ruby method by
passing the exception. It decides whether to absorb it ot store it in
@_exit_exception and properly release/stop the libuv loop, so after it
ends the @_exit_exception will be raised.
It seems to work ok. So basically I’ve avoided the longjmp while my
C libuv loop is running.
Really thanks a lot for your explanation and help.