[gclist] Finalization and death notices

Jerrold Leichter jerrold.leichter@smarts.com
Mon, 8 Oct 2001 19:16:34 -0400 (EDT)

| > >  I've suggested in the past that, if you want to be friendly to both
| > >C++ idioms and gc, you need to transparently decouple these two operations.
| > 
| > C++ almost decouples free and destruct. You can invoke the destructor
| > directly by its name. And except for a silly rule against freeing new'ed
| > objects you'd be able to free them. Write your own allocator and you can.
| > Keeping track of destructors does just require doing something better with
| > the vtable pointer than zeroing it.
| C++ completely decouples allocation, construction, destruction, and
| deallocation.  You can call operator new(), constructors, destructors,
| and operator delete() directly.

That's almost, but not quite, true.  The real problem is that delete *always*
calls the destructor.  If the object has already been destructed, you're dead.
You might think you could avoid this by having a flag set in your constructor
and cleared in you destructor - but that gives you undefined behavior:  You
aren't allowed to look at the object once it's been destructed.  Carrying the
flag in memory owned by the allocator but outside the object doesn't work
either - the destructor can't get at it, since it has no way to determine if
it's being called on a heap- or stack-allocated object, hence no safe way to
set the flag.

(There's another limitation that isn't relevant to GC, but is equally
annoying: If you have a stack-allocated object, the compiler always calls the
destructor based on the *static* type, even if the destructor is virtual.  The
same is true for all member functions, of course, but there you can forward to
the right place.  But there's no way to forward a destructor call!  This makes
it impossible to do a general Smalltalk-style "become" operation, which on its
surface is easy, as long as you can ensure the type you are trying to "become"
will fit:  Invoke the destructor, then use placement new to create the new
object "over" the remains.  If the object was on the stack, you die as soon as
it goes out of scope - unless the programmer is careful to change the dynamic
type back first.  What's odd is that you can know what destructor is the right
one to invoke - but despite the general philosophy in C++ that "when the
programmer insists on something (e.g., with a cast), trust him", in this case,
the compiler just will not listen to you.)

| And by the way, the reason I am interested in C++ is that the standard
| is coming back around to the point in the ISO cycle where we can start
| to consider extensions, including garbage collection.  The Boehm
| collector, the Great Circle collector, and various "smart pointer"
| classes are some of the existing practice we have to start with.
| The existing practice in java and Limbo is arguably relevant as well. 

In my experience, the bias against gc in the C++ world is so strong that it's
unlikely that it can be overcome.  But I'd love to be proved wrong!

							-- Jerry