[Openmcl-devel] Three questions

Gary Byers gb at clozure.com
Sat Feb 9 02:56:55 PST 2008

On Fri, 8 Feb 2008, Ron Garret wrote:

> On Feb 8, 2008, at 10:50 PM, Gary Byers wrote:
>> On Fri, 8 Feb 2008, Andrew Shalit wrote:
>>> On Feb 8, 2008, at 5:34 PM, R. Matthew Emerson wrote:
>>>> On Feb 8, 2008, at 4:34 PM, Ron Garret wrote:
>>>>> 1.  Is there a way to tell when a window has been closed?
>>>> I only know of the usual Cocoa/Objective-C ways:
>>>> * register to receive NSWindowWillCloseNotification
>>>> * implement windowWillClose: in the window's delegate
>>> This is a good example of the kind of thing that I think works better
>>> in MCL, which builds a clean Lisp layer on top of the native Mac OS
>>> libraries, rather than just providing a thin Lisp interface for
>>> directly programming the Mac OS libraries.
>> I'd forgotten that there were no memory management problems with (or
>> limitations to) MCL's approach.  Ah, truly that was a Golden Age ...
> LOL!
> Although it is certainly true that the Golden Age had its rough edges, it was 
> (if memory serves) better than the current situation where, if one tries to 
> operate on a retained handle to an NSWindow that the user has closed, CCL 
> dumps core.  (I don't mean to rag on CCL here.  From what I can tell, this is 
> a Cocoa issue.  I haven't actually tried it, but I see no reason to believe 
> that an Objective C program would behave any differently.  But what do I 
> know?)
> rg

My interpretation of that is that it's a general memory management issue:
if you close an NSWindow and that causes the NSWindow to be released
(it doesn't do so necessarily: for windows that don't have associated
NWindowControllers and don't have associated NSDocuments: see
#/setReleasedWhenClosed:) then you wind up with your hands on
something presented as a (more-or-less) first-class CLOS object
that's just had some foreign "deallocation" process applied to
it (and may have had a "reallocation" process applied to it.).
If the lisp GC scribbles over some object that we can reference,
we'd call that a GC bug; I prefer to call this "unimplemented
integration between the lisp and ObjC memory management systems".
Whatever you'd call it, the results are often unpleasant; it
isn't lisp-like to say that an object that we can reference
"got deallocated", and that generally has no meaning.  (Lisp
objects that we -can't- reference get deallocated all the time,
modulo GC bugs.)

? (defvar *w* (gui::new-cocoa-window :closable t))
? (#/setReleasedWhenClosed: *w* nil)
? (#/close *w*)
? *w*
#<NS-WINDOW <NSWindow: 0x1882e490> (#x1882E490)>
? (#/isVisible *w*)

Since we asked that *w* not be released when it was closed, it's
still a valid object (and in fact, it's still a valid NSWindow):

? (#/orderFront: *w* +null-ptr+)
? (#/setTitle: *w* #@"Lazarus")

Hmm. Closing a window doesn't necessarily have anything to do with
releasing it (decrementing its "retain count" and possibly
deallocating it.)

Let's close *w* and release it.  First, just to humor me, let's
use CCL::%INC-PTR to return a copy of *W*:

? (%inc-ptr *w* 0)
#<NS-WINDOW <NSWindow: 0x1882e490> (#x1882E490)>

Another pointer that points to the same address (#x1882E490) as *w*
is also recognized as the same NSWindow.

? (#/close *w*)
? (#/release *w*)

Now, we're sort of back to your original question, which could
be rephrased as "is there a way of telling when some NSObject
has been deallocated, given that we have to - at least occasionally -
be aware of that possibility ?

As you noted, after *w* has been released:

? (type-of *w*)

which is wrong.  Determining the type of a MACPTR (actually
trying to determine the type of the address it encapsulates) is
heuristic and a bit expensive, so the foreign type info is
cached inside the MACPTR object itself.  We could say (through
some easily-implented primitive that I don't believe is actually
implemented) "clear that cached foreign type info, and try again
to determine the type of *w*"; we could also see what happens
if we try to determine the type of some other pointer that
points the same place (encapsulates the same address) as *w*

? (type-of (%inc-ptr *w* 0))

If *w* is pointing to a block of freed memory, that seems like
the most reasonable answer.  If it's reallocated as some other
type of NSObject, we might get a different answer.  If it's
reallocated as another NSWindow, you may have to resort
to other means to determine that it's not Good Old *W* (aka
The NSWindow That Couldn't Die.)

Speaking of The Undead: one traditional OSX debugging technique which
is used for both Cocoa and Core Foundation programming is to enable
"Zombie" objects: when something would otherwise be deallocated, it
instead has its class changed to a special NSZombie class (or the CF
equivalent.)  Instances of NSZombie respond to most messages by
logging the fact that a message was sent to a zombie (and would
otherwise have been sent to a freed object).  (Of course,
#/riseFromGraveAndTerrifySuperstitiousNatives is directly implemented...)

Anyway ... is it a good thing that people doing Cocoa programming in
CCL have to occasionally be aware of this sort of thing ?  Of course
not.  I think that we all agree that this sort of thing shouldn't be
exposed to the user but there are differences of opinion about how
that should best be achieved.  I doubt that anyone would disagree that
better-integrating ObjC and Lisp memory management would be a Good
Thing (that would incidentally make this problem go away, to be
replaced with the "why is this guy holding on to NSWindow objects -
keeping them from being GCed - so long?" problem).  I have difficulty
reaching the conclusion that the fact that this integration doesn't
exist somehow makes MCL-like encapsulation a good idea; there seems to
be too much evidence to the contrary.

More information about the Openmcl-devel mailing list