In article <200705160448.l4G4mPS3027878 / sharui.nakada.kanuma.tochigi.jp>,
Nobuyoshi Nakada  <nobu / ruby-lang.org> 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.)