Weakref.ref callbacks and eliminating __del__ methods
Mike C. Fletcher
mcfletch at rogers.com
Mon Jan 24 10:35:58 EST 2005
Tim Peters wrote:
>[Mike C. Fletcher]
>
>
>>I'm looking at rewriting parts of Twisted and TwistedSNMP to eliminate
>>__del__ methods (and the memory leaks they create).
>>
>>
>A worthy goal!
>
>
Well, as of now it seems to have eliminated the last leaks in
TwistedSNMP, and that's likely going to eliminate the last of our leaks
in our products, so yes, I suppose it was :) .
>A callback is strongly referenced from the weakref(s) containing it.
>
>
Thanks.
>It would really help to give a minimal example of self-contained
>executable code. I'm not sure how you're using this code, and
>guessing takes too long.
>
>
Sorry about that. I was expecting a theoretical problem, not a
practical one, so didn't think to provide a practical example.
>The "2" there is important, just because this is an interactive
>session. The point of it was to stop `_` from holding a reference to
>the created weakref. In your code
>
> weakref.ref( self, self.close )
>
>the weakref itself becomes trash immediately after it's created, so
>the weakref is thrown away entirely (including its strong reference to
>self.close, and its weak reference to self) right after that line
>ends. Of course callbacks aren't *supposed* to be called when the
>weakref itself goes away, they're supposed to be called when the thing
>being weakly referenced goes away. So this all looks vanilla and
>inevitable to me -- the callback is never invoked because the weakref
>itself goes away long before self goes away.
>
>
Alex already scooped you on this "smack yourself on the head, Mikey". I
was assuming the weakref object was a proxy for a reference in an
internal structure; I was expecting a list of weak references that was
nothing but a series of functions to call after finalising the object.
My bad.
>I'm pretty sure it's just because there's nothing here to keep the
>weakref itself alive after it's created. You could try, e.g.,
>
> self.wr = weakref.ref( self, self.close )
>
>to keep it alive, but things get fuzzy then if self becomes part of
>cyclic trash.
>
>
Yes, so it requires an external storage mechanism and code to clean that
up... ick. __del__ looks cleaner and cleaner by comparison. You'd
think I would have noticed the huge tables of weakref objects in
PyDispatcher as I was working in there...
>>I can work around it in this particular case by defining a __del__ on
>>the Closer, but that just fixes this particular instance (and leaves
>>just as many __del__'s hanging around). I'm wondering if there's a
>>ready recipe that can *always* replace a __del__'s operation?
>>
>>
>
>In the presence of cycles it gets tricky; there's a lot of
>more-or-less recent words (less than a year old <wink>) about that on
>python-dev. It gets complicated quickly, and it seems nobody can make
>more time to think about it.
>
>
Sigh, guess I should stop listening to rumours just because they are
saying something about that for which I yearn.
>>I know I heard a rumour somewhere about Uncle Timmy wanting to eliminate
>>__del__ in 2.5 or thereabouts,
>>
>>
>
>Python 3 at the earliest. That's the earliest everything nobody can
>make time for lands <0.5 wink>.
>
>
Well, we darn well better solve it by then! Don't want memory leaks
when we're hard-wired into someone's brain.
>There are unresolved issues about how to get all this stuff to work
>sanely. The driving principle behind cyclic-trash weakref endcases
>right now is "do anything defensible that won't segfault".
>
>I'll note that one fairly obvious pattern works very well for weakrefs
>and __del__ methods (mutatis mutandis): don't put the __del__ method
>in self, put it in a dead-simple object hanging *off* of self. Like
>the simple:
>
>class BTreeCloser:
> def __init__(self, btree):
> self.btree = btree
>
> def __del__(self):
> if self.btree:
> self.btree.close()
> self.btree = None
>
>
Yes, this was the "work around" I'd implemented (well, with __call__
instead and del just calling it). Of course, that didn't actually
eliminate any __del__ methods, but oh well, if we're not losing those
until 3.x it doesn't really matter :) .
The fix for the Deferred in Twisted got a little hideous (there the
__del__ is poking around into lots of different attributes of the
Deferred. To hack that I created a descriptor class that forwards a
variable to the held object and wrapped each of the needed variables in
one of those... I'm going to have to find something a little less
baroque if I'm going to get it into Twisted... really, it doesn't look
like any of it's necessary, it's just logging an error if the Deferred
ended on a Failure. Something should be done though, leaking memory
from a core object in the framework is just a PITA.
>When a weakref and its weak referent are both in cyclic trash, Python
>currently says "the order in which they die is undefined, so we'll
>pretend the weakref dies first", and then, as at the start of this
>msg, the callback is never invoked.
>
So external storage of a weakref is basically a requirement to make it
useful. Hmm.
>The problem this avoids is that
>the callback may itself be part of cyclic trash too, in which case
>it's possible for the callback to resurrect anything else in cyclic
>trash, including the weakly-referenced object associated with the
>callback. But by hypothesis in this case, that object is also in
>cyclic trash, and horrible things can happen if a partially destructed
>object gets resurrected.
>
>
Reasonable I suppose.
>So the real rule right now is that, if you want a guarantee that a
>callback on a weakly referenced object gets invoked, you have to write
>your code to guarantee that the referent becomes trash before its
>weakref becomes trash. (And note that if you do, the callback
>necessarily will be alive too, because a weakref does in fact hold a
>strong reference to its callback.)
>
>
Guess I'll go back to the sub-object approach for all things and merely
dream of a far off weakref utopia free of all __del__ methods.
Thanks, Tim,
Mike
________________________________________________
Mike C. Fletcher
Designer, VR Plumber, Coder
http://www.vrplumber.com
http://blog.vrplumber.com
More information about the Python-list
mailing list