[gclist] Buffy finalizer slayer.

Nick Barnes Nick.Barnes@pobox.com
Fri, 11 Jun 1999 16:35:33 +0100

At 1999-06-11 13:52:01+0000, Simon Peyton-Jones writes:

> > Lets open the medieval text on the evils of finalizers. It 
> > has plenty of
> > death and destruction but watcher objects seem to solve every problem.
> I believe that the Glasgow Haskell Compiler's implementation
> of (what we call) finalisers are precisely what
> you describe as watcher objects.  There's a paper describing them
>       http://research.microsoft.com/~simonpj/papers/weak.ps.gz
> (But I could be mistaken; I'd welcome feedback.)
> Do any other systems implement watcher objects as you describe them?

Harlequin's MLWorks has weak pointers but not finalization.  The weak
pointers can be used in the manner Charles describes.  I think it's
not an uncommon use of weak pointers in systems which provide them.

I also have a low opinion of finalization, though not as low as that
expressed by Charles.  The problems of it are exactly those he
identifies: firstly establishing sound semantics and secondly making
the concept useful without providing any guarantees.

As far as the semantics are concerned, it is absolutely unacceptable
to have the collector say to the mutator "here's a dead object".  And
yet that is often the way finalization is defined, and the definitions
go further wrong from there by dwelling on dependencies between dead
objects.  Much better to have the collector say "Hey, you know those
pointers you asked me to keep an eye on?  Well, these ones now point
to objects which are only alive because that set of pointers exists."
And you need some sort of non-blocking communication between collector
and mutator (e.g. a reader/writer data structure) so that the
collector doesn't pay for the finalizers to run.  And no guarantees of
any sort about dependencies and ordering.  Ordering has to be under
the control of the mutator, and the only way to achieve that is to put
the whole business of running finalizer functions into the hands of
the mutator.

And as for the value of finalization, the value of any language
feature which says "we might run this code, but we might not,
depending on whether we get around to it" is dubious.

For instance, a program which uses finalization to ensure the
availability of system resources (by cleaning up redundant resources)
is not very reliable.  Such a program will be especially unreliable
with a generational conservative collector.  Sooner or later the wrong
integers will get on the stack at some point, and at some later point
the program will die in an unreproducible way.

Finalization is much better if you have exact collection and a
"collect the whole world now" function.  Then the mutator stands a
better chance of telling that an object has a unique pointer according
to language semantics.  But exact collection is a somewhat tricky
notion with optimizing compilers.  With enough information from the
compiler, you may be able to avoid looking at any registers, stack
slots, or object slots that contain non-pointers, but it's much harder
to avoid looking at values (registers and stack slots in particular)
that contain values which might be no longer in use according to the
language semantics.

Nick Barnes