From: "Iñaki Baz Castillo" Date: 2012-05-23T02:11:51+09:00 Subject: Re: [C ext and GVL] Why UBF() is called even if Ruby traps signals ?? 2012/5/22 Eric Wong : > Iñaki Baz Castillo wrote: >> But that's not my problem. In fact, I have a mechanism to communicate >> with UV loop at any time safely from any thread: >> >>   uv_async_send()   (thread safe) > > OK, perfect.  You can probably just call in your ubf(), nothing else. Right, I've got it :) (more below). >> So from the ubf() function I have no problem in telling UV "please do >> this now or in your next instant iteration". My problem is: what to >> say? >> >> 1) If ubf() has been called from Thread#kill then I need to run a >> function that closes all the active UV handlers so uv_run() exits. > > Can you run those cleanup handlers in the killer and not the killee? > You should Thread#join the killee, and then cleanup in the killer. The problem is that I don't know if my library user will do Thread#kill or Thread#raise or whatever :) But don't worry, Finally I've it working (more below). > On the other hand, with uv_async_send(), you can probably avoid > Thread#kill entirely.  Thread#kill can be dangerous/unreliable, > as ensure clauses will still fire, and ensure clauses can be > broken, too. I don't want to use it, but the user could do it :) >> 2) If ubf() has been called due to a signal receipt, I don't want >> uv_run() to exit. Instead the signal wakeups the UV loop and generates >> an iteration in which I call to rb_check_interrupts() for handling the >> signal in Ruby land and I'm happy. But the ubf() has been called !!! >> >> So the problem is that when a signal is received, it makes the ubf() >> function to be called and, at that point, I have no way to know, >> within the ubf() function, whether it has been called by Thread#kill >> or by a received signal. > > You're right, there's no way for the ubf() to know why it's called. > > The purpose of the ubf() is only to wake up a thread and have it > reacquire the GVL.  That's it. > > Once a thread reacquires the GVL, it can: > - run Ruby signal handlers (automatically done by VM) > - die (respond to Thread#kill, again automatically done by VM) > - give up the GVL again and resume blocking (your choice) Perfect. Once this is fully understood by me I've redesigned my code so when my ubf() function is called: - I don't know if it has been called due to a signal or due to a Thread#kill, so in my ubf() function I just create a uv_async handle (thread safe), and send it to the UV reactor. - The reactor is then wakeup, acquires the GVL and runs rb_check_interrupts(), so in case I've received a signal it is handled in Ruby land. And just it :) In the other side, now I don't assume that my ubf() must terminate my blocking function (uv_run) so what I do is basically: def MyLibrary.run @running = true uv_run_terminated_by_itself = false begin uv_run() uv_run_terminated_by_itself = true ensure @running = false unless uv_run_terminated_by_itself destroy_handles() end end And that's all. The ensure block is executed in any case (due to a signal not trapped by Ruby, a Thread#kill, ...). :) -- Iñaki Baz Castillo