[Openmcl-devel] Events, Windows, and OpenMCL
gb at clozure.com
Mon Dec 11 00:58:41 EST 2006
What do you think existing Carbon and Cocoa applications - including
MCL - do ? (Hint: they run event loops.)
A "modern" Carbon application typically installs Carbon event handlers
(callbacks) for events that it's interested in, does other initializaton,
then calls #_RunApplicationEventLoop in its initial thread. Usually,
RunApplicationEventLoop runs until the application terminates, fetching
events from the Window Server and other sources and using the Carbon
event mechanism to find and invoke handlers for events. In many cases,
"standard" (predefined) event handlers provide reasonable default
A Cocoa application typically defines some classes and methods on them
(many of which directly or indirectly relate to how events are
handled), does other initialization, then sends the global
NSApplication instance a 'run message on the initial thread.
NSApplication's run method fetches events from the window server,
determines which objects should be involved in handling them, and
invokes event-related methods on those objects. In many cases,
"standard" (predefined) methods provide reasonable default behavior.
The communication between the window server and the application
happens by means of Mach message ports (an IPC mechanism). It
probably wouldn't work too well (at the very least it would
complicate things) if multiple threads could listen for incoming
event messages and reply to those messages. I'm not too sure
how well the low-level IPC stuff would handle that, and at a
higher level it would add a fair degree of complexity to ensure
that things were serialized when they needed to. Apple chose
not to support this; in OSX, only one thread can reliably receive
and reply to low-level event messages. There may be intentional
and/or unintentional exceptions from OS release to OS release,
but it generally seems to work best if this thread is the initial
thread. (It doesn't seem that GLUT - which uses Cocoa to provide
a simple framework for OpenGL drawing and event handling - enforces
this, but I don't know whether that's intentional or not.)
You will find that Carbon and Cocoa applications start event loops
on their initial threads; few things would work if they didn't.
(That's not quite the same as "nothing": certain things, IIRC,
can be handled entirely in the window server.)
MCL also ran an event loop, but
(a) it (mostly) used an older, non-extensible event API that
involved polling (busy-waiting, a little) for events rather
(b) even though it used some cooperative threading API that
made tools like Thread Manager aware of the existence of
its threads, the OS basically sees MCL as a single-threaded
application (whose stack pointer and other registers move
around from time to time.) The good news is that things
like the Mach port to the window server can be accessesed
from any "thread" (since it's all the same thread ...);
the bad new is that other things (per-thread OpenGL context
and other graphics state, per-thread AutoreleasePools in
Cocoa) are shared by MCL threads.
There are lots of reasons for favoring the more modern Carbon/Cocoa
event models, and Apple spent a few years articulating those reasons.
There are lots of reasons for letting the OS manage threads and their
scheduling rather than trying to do this at the application level.
(Being able to use the 1023 other CPU cores that're rumored to be
not-too-far-down-the-road might also count as a compelling reason.)
The combination of these and other factors mean that you pretty much
have to commit to doing all event processing in the initial (native)
thread. Once in a while, that's confusing or overly restrictive; most
of the time, it's pretty clearly the right thing.
On Sun, 10 Dec 2006, Brent Fulgham wrote:
> In playing with the MCL 5.1 demo program, I notice that the Window class
> included in the image allows one to instantiate the window, and it
> immediately pops up:
> Welcome to the demo version of Macintosh Common Lisp Version 5.1!
> ? (defvar *T* (make-instance 'window))
> The window identifies itself as part of the MCL program (at least, no
> separate process or tray icon is created), and I continue to be able to
> interact with the MCL console to create new forms, etc.
> I've written (or rather, stolen) a window implementation based on Mikel's
> Clotho/Bosco stuff, and so I also get a nice window to pop up from my OpenMCL
> session. However, my window is "dead" in that it doesn't respond to events
> though I can update images in a graphic context embedded in the window.
> I fully understand that the problem is that the window I generated is not
> being serviced by any kind of an event/message loop. However, if I were to
> do so from the constructor of the window I would end up with a "locked"
> OpenMCL console since it would never return from the window (until the event
> loop was exited).
> So, I see a few options and I'm curious if anyone can provide
> context/suggestions for the right way to go.
> 1. Each window could be launched in its own thread which would execute the
> run-loop. I'm not sure if there would be any threading concerns here, but I
> imagine this would be a fairly simple approach. However, examining the MCL
> running image with the OS X development tool "Thread Viewer" shows no new
> threads created as I open windows. So, another possibility is:
> 2. A single event loop thread could be started if the "Carbon" or "Cocoa"
> environments were activated in OpenMCL. New windows could be created, then
> this thread could be notified to begin servicing the windows as part of its
> event loop. I'm guessing this is the approach MCL uses but of course have no
> way of knowing.
> Does anyone have any comments (or dire warnings) about either of these
More information about the Openmcl-devel