[Openmcl-devel] Techniques for identifying Cocoa API calls that should be run on GUI thread
plkrueger at comcast.net
Mon Mar 10 21:18:38 UTC 2014
On Mar 10, 2014, at 2:19 PM, Clayton Stanley <cstanley at cstanley.no-ip.biz> wrote:
> I have a Cocoa application built with Clozure and the Objective-C bridge that has been developed over the course of ccl-1.8-ccl-2.0 and OS X 10.6.8 - OS X 10.9.2. It seems that every time I upgrade OS X I find new areas of the code that use the Objective-C bridge that crash due to not being thread safe. Basically, I have to identify new areas in the code that I need to wrap with '(easygui::running-on-main thread () ...)' in order to keep things from crashing.
> On the one hand, I'm happy that my testing environment works well enough to identify these new issues during testing. On the other hand, it's not very satisfying to have the tests fail every time I upgrade OS X due to new threading issues.
> I would like to be able to better identify areas in the code that might have threading issues (i.e., that need to be run on the GUI thread), so that I can clean things up before tests start to fail, so that I have a better chance to have an error-free upgrade when moving to a new OS X version.
> For developers with a lot of experience with Cocoa/Objective-C/Clozure, how have you identified these areas? Are there any static/dynamic analysis tools or debugging flags that can identify trouble spots before crashes happen?
> If you identify trouble spots by simply running the code and testing (like I'm doing currently), when a crash occurs, how do you troubleshoot where the threading issue lies? On my setup when a threading issue occurs, I do not get very informative feedback; the GUI thread hangs, the slime thread seems to hang, I don't see anything useful in Console.app, etc. So it's basically just divide-and-conquer of the testing code (and restarting slime/:cocoa each time, grrr) until I can identify the trouble area.
> Maybe the 'right' way is for me to better understand how cocoa works, so that I can use that conceptual understanding to have a better sense where trouble spots might be when writing code. If that's the case, recommendations on reading material would be very helpful.
> Thanks for the tips,
> Openmcl-devel mailing list
> Openmcl-devel at clozure.com
I've run into similar problems often enough that I now take a shotgun approach and do pretty much everything related to creating and updating displays and event processing on the main thread. I think the last thing I ran into was when I started to use Apple's layout constraints explicitly. Creating or acting on any display object that uses them or contains another object that uses them should be done on the main thread. I suspect that one reason why you experience new problems with each new OS release is that Apple is using this same technology in more and more of its own display objects. So if you happen to be creating some object that didn't previously use these constraints internally but now does; and you do that on some secondary thread; then you'll encounter problems sooner or later. I do occasionally use secondary threads that I explicitly create when I'm doing a large amount of processor-intensive things that are "pure lisp".
It is at least theoretically possible to draw to views from secondary threads. Apple recommends the following:
"If you want to use a thread to draw to a view, bracket all drawing code between the lockFocusIfCanDraw and unlockFocus methods of NSView." I haven't actually tried that, but I presume it is a viable alternative to forcing everything onto the main thread.
I'm afraid that I'm not going to be very helpful with debugging. In my constraint interface I had so many problems (some related to threading issues, but many that weren't) that I added a whole bunch of console log messages that could be enabled or disabled to help me trace where the problems were occurring. Problems related to not running on the main thread can present in pretty subtle ways. They often appeared to me to look like a violation of how my code seemed to be ordered. That is consistent with warnings that Apple gives about using multiple threads to process events. Once I realized that most of my problems were related to the use of constraints (either explicit or implicit) and made sure that all that was done on the main thread I stopped having problems.
As far as documentation goes, you can check out:
for documentation about threading under Cocoa. There is a ton of other Cocoa documentation on Apple's website.
More information about the Openmcl-devel