[mac-lisp-ide] runloops, threads, etc.

Gary Byers gb at clozure.com
Thu Feb 5 21:47:40 UTC 2004


I just subscribed to this list a little while ago and wanted to respond
to a few things that I saw in the archives.  I probably won't be able
to attribute things too accurately, and I'm sure that I've missed
parts of the discussion and may be replying to some things out of
context.

To the best of my knowledge, it's effectively the case that only one
thread can fetch events from/communcate directly with the window
server.  Prior to Panther, this was the thread whose CFRunloop was the
"main" CFRunloop (there's still some code in OpenMCL's Cocoa demo that
tries to play around with this, and it used to be the case that
something called an undocumented function - something like
"_CFRunLoopSetMain" - to try to get around this.  CF's "main" runloop
ordinarily got created when CF was initialized, and it was associated
with the thread that did that initialization.)

Panther put an end to all this: the thread that's allowed to talk to
the Window Server is the application's initial thread (the one
that started out by calling main(), way back when) and none other.
(Any other thread can certainly try to call NSApplication's
nextEventMatchingMaskWhateverTheOtherArgsAre, but it'll get a NULL
pointer back.)

The basic model is that NSApplication's -run method loops around,
getting events from the window server and propagating them through
the responder chain.  From what I understand, lots of other things
go on between the time that the -run method asks for an event and
the time that it's delivered: raw window server events arrive;
some of them are passed to Carbon event handlers (which might in
some cases call Cocoa methods), some are related to keeping the
window server's model of what's on screen in synch with the
application's  (drawRect: methods get called at this time), etc.
All of this is (by default) happening on the distinguished, blessed
event thread.

Some of the time of course, an NSEvent will eventually get returned
to the caller.  Tim Moore and I tried some experiments about a year
ago to see if we could intercept window-specific events and dispatch
them to handlers that ran in window-specific threads (the thinking
was that this was a lot closer to McCLIM's CLX backend's behavior.)
The idea sort of worked (at least halfway). One way in which I
remember it failing was when clicking on a window close button:
the (blessed) event thread was waiting for the window-specific
thread to unlock the view focus so it could ... do whatever it
does, and the window-specific thread was waiting for a mouse-up
event that never came.  It might have been possible to think
a little harder and avoid this particular deadlock situation,
but I got the strong impression that there were lots more deadlock
situations waiting to happen.

Apple had a Tech Note or similar document whose title was something
like "AppKit is too thread-safe! Stop saying that it isn't !".  That
document enumerated a small number of reasonable-sounding guidelines;
I don't think that it's too hard to follow them carefully and still
see totally unreasonable behavior.  I was trying a few months ago
to build an NSTextView "manually" in some random (non-blessed) thread
and it kept crashing; there could have been any number of reasons
for that failure, but I eventually determined that as soon as I'd
created an NSLayoutManager the blessed (if you'll pardon the expression)
thread decided that that'd be a good time to do background glyph
layout.  (It wasn't ...).  I couldn't find a reliable way of preventing
that from happening; in Cocoa's implicit view of the world, an
NSTextView could only be constructed in the blessed event thread
by some event handler (why on earth would a compute-bound thread
want to create an NSTextView or an NSLayoutManager ?)  IIRC, I
eventually wound up PROCESS-INTERRUPTing the event thread and
asked it to make the NSTextView for me; that seemed to work fine.

I'm not sure that I'm not just violently agreeing with points that
other people have made; if people are thinking about bouncing events
around between multiple lisp threads, I hope that they're aware of
these issues and have thought of things that I'm missing.  The
conclusion I came to is that Cocoa is really designed to have just
about all of the interesting UI stuff happen in its blessed thread,
and if any other approach is possible it may go so far against the
grain as to be impractical.


Gary Byers
gb at clozure.com




More information about the Mac-lisp-ide mailing list