On Nov 2, 2004, at 12:41 AM, Leopold Toetsch wrote:

When we have objects with finalizers, we have to run the finalizers in
order from most derived down the parent chain.

Maybe, but not necessarily. The case of loops means that we cannot always do this cleanly (no "top" of the chain), and the fact that finalizers may modify the topology of the object graph also makes this a bit ill-defined. There are several possible approaches, ranging from very conservative to very aggresive. Two real-world approaches already used are: make no guarantees about finalization order--only guarantee that a finalizer will be called at some point after an object becomes unreferenced, with no guarantees about ordering, and no guarantees that it will not be called if the object has already be re-rooted (Java); or, do graph-ordered finalization, and never call the finalizers of objects which are in loops (the Boehm collector does something along these lines, I believe).


So as you state, we've to
defer destruction, store these objects with finalizers somewhere, sort
them, run user code to finalize objects and so on.

Other objects are impacted as well--any object reachable from a finalizable object must wait for reclamation until finalizers have fired.


Doing that all in the destroy vtable is tedious especially, when user
code is involved too. The finalize vtable is overridable, the destroy
isn't. A split makes this functionality much cleaner.

I think what's bothering me about this is that I think part of the idea is to move closing of filehandles and such into destroy()--right? That sounds good--don't get rid of this resource until it's certain nothing will try to use it (eg, things which want to log a message during their finalization would use a filehandle). But I don't think this really solves it. For one thing, there will be similar-in-functionality code coming up in HLL's (doing some orderly shutdown of connections, which involves more than just closing the filehandle), and this will have to happen in a finalizer (since that's the only HLL option), which will feel "too early" in some cases. And even in a destroy(), you might want to do some I/O, and whether this will work will depend on whether the filehandles have been closed yet, and the same ordering concerns re-emerge.


So I think the split would help, in a sense, but only be a partial solution to an unsolvable problem, and therefore maybe is not very elegant. So I'm a bit on the fence.

(And in my parlance, just to be clear: "finalizers" are called after an object becomes un-referenced, and could re-root an object; "destructors" are called when the object is actually having its memory re-claimed--when it is going away for sure. No user code can be called from a destructor in the Parrot case, because of the possibility of re-rooting, and because of other constraints which may exist at destroy-time.)

JEff



Reply via email to