Uh oh!
There was an error while loading. Please reload this page.
bpo-42972: Fully implement GC protocol for functools keywrapper and partial types - #26363
Conversation
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
ce657de to
f6e6676Comparef6e6676 to
6d7cdafCompareerlend-aasland
commented
May 27, 2021
Thank you so much for reviewing and pointing out the weakness in the LRU cache :) |
| Py_VISIT(pto->args); | ||
| Py_VISIT(pto->kw); | ||
| Py_VISIT(pto->dict); | ||
| Py_VISIT(Py_TYPE(pto)); |
There was a problem hiding this comment.
I would prefer to always start by visiting the type in all traverse functions. So same remark for other traverse functions.
I like to look into partialobject structure and visit them by their definition order. Technically, ob_type is the first one :-D
There was a problem hiding this comment.
Sure, I'll apply to all remaining PR's :)
| {Py_tp_members, partial_memberlist}, | ||
| {Py_tp_getset, partial_getsetlist}, | ||
| {Py_tp_new, partial_new}, | ||
| {Py_tp_free, PyObject_GC_Del}, |
There was a problem hiding this comment.
Would it be possible to keep it and maybe only change that in 3.11 (in a separated PR)? Type inheritance is more complex than what it looks. See https://bugs.python.org/issue43770 for examples of bad surprised that I got when trying to use the default implementation of tp_getattro and tp_setattro.
| PyObject_Free(ko); | ||
| PyObject_GC_UnTrack(ko); | ||
| (void)keyobject_clear(ko); | ||
| PyObject_GC_Del(ko); |
There was a problem hiding this comment.
IMO here it's ok to hardcode PyObject_GC_Del(), but in general I would suggest to call tp->tp_free(ko); in case tp_alloc/tp_free is overriden in a subclass. To make the code more consistent, I would suggest to always call tp_free in dealloc functions. What do you think?
There was a problem hiding this comment.
I agree :) Consistency FTW
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
This reverts commit 6d7cdaf.
erlend-aasland
commented
May 27, 2021
@pablogsal, @shihai1991, @methane: I've reverted the LRU cache optimisations as per @vstinner's request. |
| Py_VISIT(link->result); | ||
| Py_VISIT(Py_TYPE(link)); | ||
| return 0; | ||
| } |
There was a problem hiding this comment.
A lru_list_elem_clear() function would also be needed to implement the GC protocol, no?
There was a problem hiding this comment.
A lru_list_elem_clear() function would also be needed to implement the GC protocol, no?
I was under the impression traverse was enough. I may be wrong.
There was a problem hiding this comment.
Without clear, the ref count cannot each zero, and the GC cannot break cycles: https://devguide.python.org/garbage_collector/#destroying-unreachable-objects
cc @pablogsal
There was a problem hiding this comment.
_lru_list_elem is not a standalone type. It is an internal of _lru_cache_wrapper.
lru_cache_tp_traverse()visitslru_list_elem.lru_cache_tp_clear()clearslru_list_elem.
That's why we can keep _lru_list_elem non-GC type.
vstinner
commented
May 28, 2021
If you want to hide the Right now, the And the current code for it in But you still need to add Since the lru_cache is way more complicated, I suggest to restrict this PR to functools.KeyWrapper and partial type, and write a second PR just for the lru_cache type. |
This reverts commit fc39917.
Reverting fc39917 should be sufficient; no need to open a new PR. UPDATE: The PR now adds GC support to the KeyWrapper and partial types, and modifies |
methane
commented
May 28, 2021
Since _lru_list_elem is not GC-tracked type,
Current PR breaks circular reference correctly. See #26363 (comment) Since _lru_list_elem is hidden and immutable now, it is OK to remove |
erlend-aasland
commented
May 28, 2021
It's not immutable; only static types and heap types with the |
vstinner
commented
May 28, 2021
@methane: "Since _lru_list_elem is not GC-tracked type, gc.get_objects() don't leak _lru_list_elem regardless lru_cache_tp_traverse() visit _lru_list_elem or not." It doesn't matter if the type implements the protocol or not. As soon as there is a Py_VISIT() call on it, it is exposed in gc.get_objects(). I tested the current PR (commit 6313247, Revert "Revert LRU cache element optimisation"): Example: Here is the hidden functools._lru_list_elem type. |
vstinner
commented
May 28, 2021
@erlend-aasland: Can you please leave LRU unchanged in this PR and create a new PR just for LRU? The LRU problem sounds complicated and IMO it deserves its own change. |
Of course. Give me 2 minutes. UPDATE: Done. |
erlend-aasland
commented
May 28, 2021
erlend-aasland
commented
May 28, 2021
Thanks for your insights and reviews, everyone! |
miss-islington
commented
May 28, 2021
Thanks @erlend-aasland for the PR, and @vstinner for merging it 🌮🎉.. I'm working now to backport this PR to: 3.10. |
bedevere-bot
commented
May 28, 2021
GH-26424 is a backport of this pull request to the 3.10 branch. |
…artial types (pythonGH-26363) (cherry picked from commit 8994e9c) Co-authored-by: Erlend Egeberg Aasland <erlend.aasland@innova.no>
https://bugs.python.org/issue42972