|
From: Jonathan P. <jp...@dc...> - 2005-04-14 16:20:09
|
On 14 Apr 2005, at 16:13, kimura wataru wrote:
> RubyCocoa apps with ruby threads normarlly work fine, like
> SimpleApp.app
> in examples. But in this case, calling NSApplication#endSheet fails.
>
> I found NSApplication#endSheet succeeded when the method was invoked
> with
> new NSThread. I guess a sheet expects to receive "endSheet:" from other
> run-loop.
It's not the secondary run loop that's the problem, but rather
unfortunate interactions between Ruby's thread scheduling and Cocoa's
exception handling. Starting a new NSThread certainly solves the
problem (since it uses a separate exception stack).
I'll try to explain:
Cocoa's exception handlers (NS_DURING etc) work by maintaining a
per-pthread list of exception handlers. Therefore, when native code
does:
NS_DURING
// some code
NS_HANDLER
// handler code
NS_ENDHANDLER
what actually happens is
if (setjmp()) {
// add jmp_buf to exception handler list for the current pthread
// some code
} else {
// handler code
}
remove jmp_buf from exception handler list for the current thread
The problem arises under the following circumstances (not related to
endSheet at all):
The main run loop calls a ruby callback, which spawns a thread.
The secondary thread invokes an objective-c method which makes a
callback to a ruby object.
Ruby ruby callback runs long enough to yield back to the main thread.
At this point, the NS_DURING exception handler for the objc method
call (from ocm_perform) is on the exception handling stack, and if the
main run loop handler returns (popping its exception handler) it'll
find the wrong handler at the top of the stack. This causes a warning
message, and then the program crashes.
The following small program demonstrates this fairly repeatedly
(doesn't happen every time though).
The reason for this problem is that Ruby thread switching changes the
stack, but Cocoa isn't aware of this.
#############################################################
require 'osx/cocoa'
class ExceptionTest < OSX::NSObject
def foo
Thread.new do
self.performSelector_withObject(:bar,nil)
end
end
def bar
Thread.pass
end
end
a = ExceptionTest.alloc.init
t = a.performSelector_withObject(:foo,nil)
t.join
#############################################################
The error output looks like this:
2005-04-14 17:14:35.409 ruby[15510] *** Exception handlers were not
properly removed. Some code has jumped or returned out of an
NS_DURING...NS_HANDLER region without using the NS_VOIDRETURN or
NS_VALUERETURN macros.
/Library/Frameworks/RubyCocoa.framework/Versions/A/Resources/ruby/osx/
objc/oc_wrapper.rb:17: [BUG] Bus Error
ruby 1.8.2 (2004-12-25) [powerpc-darwin7.7.0]
|