NSApplication, AppleEvent and CFRunLoop source strange interaction
NSApplication, AppleEvent and CFRunLoop source strange interaction
- Subject: NSApplication, AppleEvent and CFRunLoop source strange interaction
- From: Brian Tietz <email@hidden>
- Date: Tue, 21 Apr 2009 19:10:57 -0700 (PDT)
I'm working on a cross-platform framework, and am trying to finish up a parallel but portable message dispatch mechanism in the framework when its main thread is running in NSApplication. To this end, I'm using CFRunLoopSourceCreate/CFRunLoopAddSource as the framework message trigger because I can safely trigger the source from other threads using that mechanism. It is working nicely, except for the quit sequencing. In Linux/GTK, the analogous mechanism is installing the read end of a pipe using g_io_add_watch, writing to the pipe when a message is posted to my framework, and it works perfectly.
For the Cocoa implementation, I have implemented an applicationShouldTerminate app delegate notification handler which posts a quit request to my framework (triggering it by way of the CFRunLoopSource) and returns NSTerminateCancel. That last detail is very unfortunate, but if I return NSTerminateLater the app goes into a modal state and neglects the CFRunLoop, with my framework's quit request not being received as a consequence.
If I use the dock's application menu to issue a quit request, the kAEQuitApplication Apple event ends up in applicationShouldTerminate, then after returning NSTerminateCancel the framework message is processed by way of the run loop source. If the application decides that it can indeed quit, it sends [ns_application stop: ns_application] and the application quits successfully, returning through __CFRunRoolRun, ..., _DPSNextEvent, ..., -[NSApplication run], back out to NSApplicationMain. All good...
The problem arises if the application, in the course of doing its thing decides to quit where its activity has been recently isolated to my framework through the CFRunLoop source. My custom quit request message is handled in that context, and I send [ns_application stop: ns_application], just as if the quit request had originated from an Apple event. Even though the call stack looks identical in these two cases (apple event from dock quit menu item or internally initiated quit), the run loop doesn't actually quit in the latter case. I tried following sending the stop message with an explicit call to CFRunLoopStop, with no luck. There seems to be something special about the context of having received kAEQuitApplication.
Based on these observations, I tried having my framework, upon making a quit decision, post kAEQuitApplication to itself. Unfortunately, AESendMessage crashes inside of findPortByPSN -> AECSD_LookupAppleEventPortByPSN -> mach_msg -> mach_msg_trap. I speculate that AESendMessage normally uses two IPC transactions, one to get the port and one to post to the port. Speculating again, but because the application is trying to talk to itself, it can't respond to its own request for the port and the transaction breaks down.
Does anyone have a suggestion for a better way to solve the won't quit problem, or a better (thread safe) mechanism for triggering my framework than a CFRunLoop source?
Also, a somewhat off-topic question from an objective-C newcomer. I see this in the successful quit case:
_DPSNextEvent -> AEProcessEvent -> ... -> -[NSApplication _shouldTerminate] -> -[_docController:shouldTerminate:] -> -[NSAppDelegate applicationShouldTerminate:]
I wanted to try simply posting (asynchronously) a quit request right in the Mac Objective-C framework, specifically to _shouldTerminate, but I don't see how to do that. The only built-in Objective-C asynchronous messaging I found involved NSNotificationQueue, and it didn't seem like the right way to go. I would speculate that NSNotificationQueue would have to use a similar triggering mechanism to what I'm doing with the CFRunLoop source, and would break in the same manner. Am I wrong about NSNotificationQueue or overlooking a built-in Objective-C feature here? If it exists, it seems like that would also work around the modal state neglects run loop source problem I described earlier when returning NSTerminateLater from applicationShouldTerminate.
Thanks,
Brian Tietz
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden