[Rubycocoa-devel 329] Re: RBException_RuntimeError - continuation called across trap

Back to archive index

Jonathan Paisley jp-ww****@dcs*****
Mon Aug 7 06:22:26 JST 2006


On 4 Aug 2006, at 20:03, Tim Burks wrote:


> But unfortunately it's not that simple. When I tested this, my
> application exited with "RBException_RuntimeError - continuation
> called across trap".
>
> Looking into it, I saw that each Objective-C call to Ruby is wrapped
> in a call to rb_protect so that Ruby exceptions can be caught.  But
> this also sets a "cont_protect" variable that tracks the contexts in
> which a continuation is created and called, and these contexts must
> be the same for the call to be successful.  Unfortunately, each
> rb_protect call sets cont_protect to a unique value.
>
> So it looks like we cannot use continuations created in one
> invocation from any other invocation.
>
> Has anyone tried to remove this limitation?

I had more of a read through eval.c to see what it does with  
continuations. I think that the reason for the cont_protect stuff  
you've identified is to prevent continuations being used across  
extension library code, because there could then be scope for  
resources not being freed properly.

In Objective-C land, things are particularly problematic due to the  
autorelease pool and exception handlers, particularly because the C  
stack changes between Ruby threads. This is exactly the problem we've  
encountered with using Ruby-level threads in an ObjC application. My  
patch to the ruby interpreter explicitly switches the autorelease  
pool and exception handler stack in and out on Ruby thread switches.

The same patch code would kick in when using continuations, since  
continuations are implemented in Ruby by creating a Ruby thread.

This raises an issue that I hadn't been aware of before. Our current  
usage of rb_protect also handles 'throw :XXX', but incorrectly  
packages them up as a ruby exception. It's vital that we catch any  
longjmp-ing done by the ruby interpreter, in order to honour any  
objective c exception handlers and finally clauses. It seems,  
therefore, that symbols thrown in ruby-land should probably be  
wrapped in a special NSException and rethrown where appropriate.

Unfortunately, the TAG_* constants defined in eval.c are not public,  
and it is these that are the value of the 'state' byref argument to  
rb_protect. The TAG_* constants tells us what kind of thing has  
happened. For example, TAG_RAISE is used for exceptions and TAG_THROW  
is used for thrown symbols. Furthermore, the symbol that has actually  
been thrown is stored in the private 'struct tag.dst' field. So,  
there's no easy way to wrap up the 'throw' properly in an  
NSException, because we can't find what symbol was thrown.

It might be possible to just wrap up the 'state' value in a special  
exception, and call rb_jump_tag() at the appropriate time (upon  
return from an objc method invocation from the bridge).

I think exception handling/wrapping is something that needs to be  
looked at in more detail anyway... The starting point would probably  
be some solid examples along with explanations of current and desired  
behaviour.

Getting back to the continuation issue... We can't use rb_protect if  
you want to use continuations. We could use rb_rescue2 instead, but  
this doesn't provide safety for throw/catch across objective c code.  
If you're willing to ensure that throw/catch is never used across  
objective c code, then that is a possible compromise. However, the  
thread scheduling patches to RubyCocoa and Ruby will still be required.


Sorry for the long-winded reply. Hopefully you can make some sense  
out of it! I think implementing the thing using threads would  
probably be the most straightforward solution.

Cheers,
Jonathan





More information about the Rubycocoa-devel mailing list
Back to archive index