Uh oh!
There was an error while loading. Please reload this page.
Systematically replace __del__ with weakref.finalize() - #246
Conversation
leofang
left a comment
There was a problem hiding this comment.
Thanks, Ralf! A general comment: We can't use self.close as the finalizer because it'd still hold a reference to self (see @wence-'s original #87 (comment)). The most simple thing we could do is to move it out of the class, so instead of
classSomething:
def__init__(self, ...):
self._handle= ...
weakref.finalize(self, self.close)
defclose(self, ...):
ifself._handle:
# do somethingwe do
# note the 1st arg name is still "self", to mimic a methoddef_close_something(self, ...):
ifself._handle:
# do somethingclassSomething:
def__init__(self, ...):
self._handle= ...
weakref.finalize(self, _close_something)rwgk
commented
Nov 16, 2024
Ah, confirmed, by looking more. Yesterday I was jumping to a wrong conclusion, twisty explanation omitted. I'll try again. |
rwgk
commented
Nov 16, 2024
I think I found a nice pattern to avoid reference cycles. This is my updated toy example: The trick is to introduce a level of indirection, I still need to adopt that pattern in this PR. |
Corresponding demonstration of finalize behavior (immediate cleanup): https://github.com/rwgk/stuff/blob/f6fbd670b8376003c7767c96538d8ab0b1f49d96/random_attic/weakref_finalize_toy_example.py
rwgk
commented
Nov 30, 2024
Uh oh!
There was an error while loading. Please reload this page.
shwina
commented
Dec 2, 2024
Thanks, Ralf! I might be missing some context here, but I'm not sure how to interpret the output of the |
shwina
commented
Dec 2, 2024
Spoke with Ralf offline and now I have more clarity. Some notes from our call:
|
shwina
left a comment
There was a problem hiding this comment.
I think this is a clever approach around the limitation of weakref.finalize not being able to accept a bound method as a finalizer.
Approving with one caveat/question. Why don't we just handle the destruction of stream, event, etc., in the Cython, i.e.,:
cdefclassCUStream:
def__cinit__(self, ...):
# presumably we have a __cinit__def__dealloc__(self):
# why don't we just call cuStreamDestroy in a __dealloc__?rwgk
commented
Dec 2, 2024
That didn't cross my mind, sounds interesting, but I don't know enough Cython to know if that'll work. @leofang I'd be happy to play with that, as one way for me to get deeper into Cython, if you think that makes sense. |
leofang
commented
Dec 2, 2024
If the subject is the low-level
If the subject is the pythonic
|
shwina
commented
Dec 2, 2024
We could have both:
|
leofang
commented
Dec 2, 2024
Right, it'd be a refactoring to what's done in this PR. I am fine with pursing this at a later time (this PR looks fine to me and gets the job done), but if we lower the entire |
leofang
left a comment
There was a problem hiding this comment.
Q: @rwgk IIRC you mentioned finalizer.detach() is needed if close() is explicitly called (to avoid double free). I assume it's no longer true because the finalizer becomes a no-op due to the pre-conditions such as if self.handle is not None?
Uh oh!
There was an error while loading. Please reload this page.
rwgk
commented
Dec 2, 2024
Yes, the way I think about it: By way of the intermediate object ( |
rwgk
commented
Dec 2, 2024
/ok to test |
Closes#141
See #141 (comment) for the rationale.
This
_MembersNeededForFinalizepattern is applied systematically in cuda_core:The weakref_finalize_toy_example.py demonstrates conclusively that the finalizer runs immediately when the main object (
ShopKeeperin the example) goes out of scope; i.e. the_MembersNeededForFinalizepattern does not create reference cycles:For simple cases (e.g. _event.py) the
_MembersNeededForFinalizepattern might seem more complex than necessary, but note that it is generally safer. See commit 26ddbf6 for a side-by-side comparison. Note thatself._finalizer.Detach()is needed in the slightly simpler alternative. The need for this is likely to be overlooked when the code is extended or refactored in the future and could lead to situations akin to a double-free that may only be discovered in production.