[gclist] Buffy finalizer slayer.

Hans Boehm boehm@hoh.engr.sgi.com
Fri, 11 Jun 1999 11:01:36 -0700


On Jun 11,  8:03am, Charles Fiterman wrote:
> So instead of running a finalizer lets send out a death notice. I call
> objects receiving a death notice watcher objects. One way of building a
> watcher object is to include a weak pointer to the object to be shut down.
> There are more efficient schemes but they require collector changes and
> weak pointers are very safe and may have other uses. The watcher object
> lives on a list of watcher objects and after each collection the user scans
> the list for watcher objects that got a death notice in the form of a
> zeroed weak pointer. Perhaps the collector could increment a count of weak
> pointers zeroed which the user could decrement.
>
I believe this is essentially the PARCPlace Smalltalk scheme.  I also believe
it's essentially equivalent to topologically ordered finalization (see below).
 If you prefer it, fine.  There is a minor problem with it that you need a
callback "after each GC", a concept that's hard to define in general, and
performance requires you not get the callback too often.  You need to make sure
that the interface maps reasonably cleanly onto incremental collectors, and
perhaps even collectors that partially rely on reference counting.

If I recall, Dybvig's guardians are not topologically ordered, and thus
different.  (If you don't believe that "watchers" are topologically ordered,
see below.)

Why it's essentially equivalent to topologically ordered finalization:

I will generally need a (strong) pointer form the watched object to the watcher
object, since the finalizer is likely to need access to some of the state
manipulated by other operations on the object.  (Even if it's not strictly
needed, it affects reachability only if the finalizer needs information that
the object otherwise doesn't, an unlikely scenario.)

The watcher will need pointers to anything needed during finalization.  There
isn't really a need for anything to refer to the watcher except the object
itself, and the list of objects to scan after notification that a GC has
completed.  If we simply turn the watcher object into a finalizable object, and
forget about the list of objects to be scanned, the watcher object will become
only finalizer reachable precisely when the watchee becomes inaccessible.

Similarly we can go from a topological order finalization scheme to the
watcher-based scheme by splitting objects into two, with the watcher containing
data needed by finalization, the watchee containing any other data, and a
pointer to the watcher.  Object pointers turn into watchee pointers.  (This
split is a good idea anyway, since it reduces finalization dependencies.)

Note that this doesn't eliminate problems with finalization cycles.  It reduces
their occurrence to the genuine problem cases.  But I can also do that by just
splitting objects with topologically ordered finalization.  If I previously had

 ->
A   B
 <-

I may now get

A watchee  <-    -> B watchee
             \  /
    |	      \/        |
    v         /\        v
             /  \
A watcher   /    \   B watcher

i.e. each watchee points to its watcher, and each watcher points to the other
watchee, since it needs to invoke it from its finalizer.  Thus I again have a
cycle.

(In other cases, I will end up with a cycle only between the watchees, and then
I'm fine.  But I could have acheived the same result with object splitting and
topological finalization.)

Similarly long finalization chains can still exist (though I have yet to see a
real case where those are a problem no matter what the finalization model).

Hans

-- 
Hans-J. Boehm
boehm@sgi.com