[Openmcl-devel] autorelease pools

Gary Byers gb at clozure.com
Tue Sep 7 02:01:52 PDT 2004

On Mon, 6 Sep 2004, alex crain wrote:

> Could someone give me a short explanation on when I need to create an
> autorelease pool?
> My understanding is that I get one for free when I boot up from AppKit
> but I need to
> create one every time I create a new thread. I also should create one
> when I'm building
> transient structures or they won't get released until the thread exits.

The stuff that's done for you automatically is done "for convenience",
so that you don't see messages indicating that no autorelease pool is
in place. The initial listener thread acquires an autorelease pool, and
subseqeuent attempts to create listeners in the IDE also acquire them.
This enables you to type things into those listeners:

? (send some-object :message-that-causes-lots-of-autoreleases)

without hearing that those objects are being leaked; there are also
some colon-commands added to the REPL that'll enable you to look at
and manipulate the current listener thread's pool.

The Cocoa event thread does something like:

      (let* ((event (get-next-event-matching-mask)))
        (process-event event))))

in the application's "run" method.  (This code is actually in ObjC, but
the "run" method is documented to behave like this.)

Other threads that might cause autorelease messages to be sent (basically,
any thread that calls ObjC code ...) needs to wrap WITH-AUTORELEASE-POOL
around at least the outermost such call.

> Where I'm having trouble is with the idea that all the NS objects
> have lisp wrappers so I would intuitively think that when the lisp
> objects get GC'd they would clean up the COCOA objects as well, but
> maybe not. Is lisp side memory management totally separate from the
> COCOA side?

NS objects don't really have much of a wrapper around them.  A MACPTR
looks something like:

<header word> | address | domain | type

The <header word> is just something that tells the type system that
this is a MACPTR; the "address" is some arbitrary 32-bit value.  The
"domain" and "type" fields are basically used by the lisp type system;
they're initially set to values that indicate "we don't know whether
this pointer is anything but a random, untyped pointer".  When such
a pointer is first encounterd (by TYPE-OF or CLASS-OF or a few other
things), some heuristic tests are used to see if it can be classified
as an ObjC object; if so, the "domain" and "type" fields are updated
to cache this information, and TYPE-OF/CLASS-OF will effectively say
"this is an instance of the NS:NS-WINDOW class" instead of "this is
just some random MACPTR".  (One of the attempts to speed up Hemlock
typing last week involved skipping some of the "heuristic tests"
in cases where we "know" that we've got a pointer to an ObjC instnace
in our hands - e.g., the SELF variable in a method callback.)

This scheme (sort of) works for long-lived NSObjects.  The deep dark
secret is that the lisp runtime isn't really involved in the memory
management of that NSObject; if an address is freed by the ObjC
runtime, the cached information in any MACPTR that points to that
address is no longer valid.  Randall Beer and I had (mostly) worked
out a scheme that integrated memory management a bit better; it
involves "canonicalizing" pointers to NSObjects, retaining the
canonical pointer, and using termination/finalization to release
the canonical pointer when it was no longer referenced.  Getting
this to work and scale well is tricky, and some parts of the scheme
triggered nightmare scenarios ("zombie" threads that don't die and
can't respond to PROCESS-INTERRUPT, a few other things that I'd
rather not be reminded of ...)

(Another, more ambitious scheme, is to hook into the ObjC runtime very
early, and take more control of the underlying ObjC (and CF) memory
management stuff.)

In practice, this all means that "typed MACPTRS" aren't really
first-class lisp objects: they don't exist as long as it's possible to
reference them, and it isn't always safe to treat them as if they
were.  I think that you have to try fairly hard to expose The Awful
Truth: you usually write methods that deal with objects that're
retained and not going anywhere during the body of the method.  You
-can- lose - retaining pointers to ObjC objects that're autoreleased
is one way that comes to mind - and it doesn't take extraordinary
effort to lose, but it does take effort.

> _______________________________________________
> Openmcl-devel mailing list
> Openmcl-devel at clozure.com
> http://clozure.com/mailman/listinfo/openmcl-devel

More information about the Openmcl-devel mailing list