From: neverland@... (Pete) Date: 2007-05-16T15:40:08+09:00 Subject: Re: rb_yield(), semaphores and 'break' In article <200705160448.l4G4mPS3027878@sharui.nakada.kanuma.tochigi.jp>, Nobuyoshi Nakada wrote: >Hi, > >At Wed, 16 May 2007 12:05:04 +0900, >Pete wrote in [ruby-talk:251758]: >> >Have you tried rb_ensure()? >> Yes. That's what I was referring to. I can 'capture' the break >> with a function pointed to by an rb_ensure call, but the problem >> is that the 'body' part of that call is *still* sitting on the >> semaphore, so that if I release it from the ensure section, it >> is too late -- the stack is already popped. It tries to return >> at that point, and Boom! At least that's what seems to be happening. >> Unless someone can correct me... > >Still I'm not sure about your situation. What do you mean by >"sitting" and "popped"? To where trying to "return"? > >Can't you show simplified code? Guess I'm being too cryptic... (:-/) I'll try with a *very* stripped down version of my code. Hopefully I haven't stripped out anything relevant! Here's the skeleton: void callback_from_handler(....){ if (!rb_block_given_p() || !receiving || ...) return; // do nothing unless active VALUE bev = rb_class_new_instance(... cBMidiEvent); // event data copied to new obj VALUE res = rb_yield(bev); // pass event to code block if (RTEST(res)) { // not nil or false -- used as termination signal receiving = false; release_sem(); // tell 'do_each' to wake up } } VALUE bmidi_ensure(VALUE self) { if (receiving) { // only if not terminated normally (details omitted...) release_sem(); } receiving = false; return self; } VALUE bmidi_doeach(VALUE self) { receiving = true; acquire_sem(); // sleep until woken (timeout etc omitted) return self; } VALUE bmidi_each(VALUE self) { // code block passed to this -- handled by ruby rb_ensure(RUBY_METHOD_FUNC(bmidi_doeach), self, RUBY_METHOD_FUNC(bmidi_ensure), self); return self; } (Note that in fact the callback is a virtual member fuction of an OS class, and that the Ruby class for 'bmidi' wraps this with a Data_Wrap_struct. All that seems to work, so I've left it out. 'bmidi_each' is the only method to be published to the class (in the unshown 'Init_bmidi()').) Now my understanding of what happens is that bmidi_each gets invoked from the ruby level (with an associated block that gets stashed as the "current" one). The rb_ensure() within sets up some kind of linkage to make sure the 'ensure' section will get called, and then calls 'bmidi_doeach'. This does little but mark things as active and then goes to sleep on the semaphore. Once this has been done, events arriving at the callback will get passed to the current block through rb_yield. In *normal* operation, either the semaphore will time out (mechanism not shown) or yield will get some 'true' value back from the block to signal termination. Either way, the semaphore is released, bmidi_doeach returns, bmidi_ensure is invoked (but does nothing because the flag has been cleared), and everything is fine. On the other hand, if there is a 'break' in the block, bmidi_ensure gets called at once, but bmidi_doeach is still hanging on the locked semaphore. The semaphore has to be released (otherwise the function would block forever), but as (I am assuming) the system is now no longer expecting a return from that function there is an immediate segmentation fault. Maybe I have misunderstood how Ruby handles a break-return, but the segfault is real! Hope that makes my difficulty clearer. -- Pete -- -- ============================================================================ The address in the header is a Spam Bucket -- don't bother replying to it... (If you do need to email, replace the account name with my true name.)