[Python-Dev] finalization again

Tim Peters tim_one@email.msn.com
Fri, 10 Mar 2000 03:18:29 -0500


This is getting to be fun, but afraid I can only make time for the first
easy one tonight:

[Tim, conjures a horrid vision of finalizers installing new __del__ methods,
 then sez ...
]
> The scheme above is meant to be bulletproof in the face of abuses even
> I can't conceive of <wink>.

[Guido]
> Are you *sure* your scheme deals with this?

Never said it did -- only that it *meant* to <wink>.  Ya, you got me.  The
things I thought I had *proved* I put in the numbered list, and in a rush
put the speculative stuff in the reply body.  One practical thing I think I
can prove today:  after finding SCCs, and identifying the safe nodes without
predecessors, all such nodes S1, S2, ... can be cleaned up without fear of
resurrection, or of cleaning something in Si causing anything in Sj (i!=j)
to get reclaimed either (at the time I wrote it, I could only prove that
cleaning *one* Si was non-problematic).  Barring, of course, this "__del__
from hell" pathology.  Also suspect that this claim is isomorphic to your
later elaboration on why

    the objects on T at this point cannot be resurrected by a finalizer
    that runs, since they aren't reachable from any finalizers

That is, exactly the same is true of "the safe (SCC super)nodes without
predecessors", so I expect we've just got two ways of identifying the same
set here.  Perhaps yours is bigger, though (I realize that isn't clear;
later).

> Let's look at an example.
> (Again, lowercase nodes have no finalizers.)  Take G:
>
>   a <=> b -> C
>
> [and cleaning b can trigger C.__del__ which can create
>  a.__class__.__del__ before a is decref'ed ...]
>
> ... and we're halfway committing a crime we said we would never commit
> (touching cyclical trash with finalizers).

Wholly agreed.

> I propose to disregard this absurd possibility,

How come you never propose to just shoot people <0.9 wink>?

> except to the extent that Python shouldn't crash -- but we make no
> guarantees to the user.

"Shouldn't crash" is essential, sure.  Carry it another step:  after C is
finalized, we get back to the loop clearing b.__dict__, and the refcount on
"a" falls to 0 next.  So the new a.__del__ gets called.  Since b was visible
to a, it's possible for a.__del__ to resurrect b, which latter is now in
some bizarre (from the programmer's POV) cleared state (or even in the bit
bucket, if we optimistically reclaim b's memory "early"!).

I can't (well, don't want to <wink>) believe it will be hard to stop this.
It's just irksome to need to think about it at all.

making-java's-gc-look-easy?-ly y'rs  - tim