GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — bpo-42972: Fully implement GC protocol for functools keywrapper and partial types by erlend-aasland · Pull Request #26363 · python/cpython

Conversation

@erlend-aasland

@erlend-aasland erlend-aasland commented May 25, 2021 •
edited by bedevere-bot
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor

https://bugs.python.org/issue42972

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@erlend-aasland
erlend-aasland requested a review from rhettinger as a code owner May 25, 2021 20:14
@the-knights-who-say-ni the-knights-who-say-ni added the CLA signed label May 25, 2021
@bedevere-bot bedevere-bot added the awaiting review label May 25, 2021
@erlend-aasland
erlend-aasland requested a review from pablogsal May 25, 2021 20:14
@erlend-aasland erlend-aasland added skip news needs backport to 3.10 labels May 25, 2021
shihai1991
shihai1991 reviewed May 26, 2021
View reviewed changes
Comment thread Modules/_functoolsmodule.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

@shihai1991
shihai1991 requested a review from methane May 26, 2021 10:19
methane
methane reviewed May 26, 2021
View reviewed changes
Comment thread Modules/_functoolsmodule.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

@erlend-aasland
erlend-aasland force-pushed the bpo-42972/functools branch 2 times, most recently from ce657de to f6e6676 Compare May 26, 2021 20:48
@erlend-aasland
erlend-aasland marked this pull request as draft May 26, 2021 20:51
Erlend E. Aasland added 2 commits May 27, 2021 01:58
Erlend E. Aasland
bpo-42972: Fully implement GC protocol for functools types
8ef6e98
Erlend E. Aasland
Address review
6d7cdaf
@erlend-aasland
erlend-aasland force-pushed the bpo-42972/functools branch from f6e6676 to 6d7cdaf Compare May 26, 2021 23:58
@erlend-aasland
erlend-aasland marked this pull request as ready for review May 27, 2021 00:01
shihai1991
shihai1991 approved these changes May 27, 2021
View reviewed changes

@shihai1991 shihai1991 left a comment

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your update :)

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

erlend-aasland reacted with hooray emoji
All reactions
  • 🎉 1 reaction
@bedevere-bot bedevere-bot added awaiting core review and removed awaiting review labels May 27, 2021
@erlend-aasland

erlend-aasland commented May 27, 2021

Copy link
Copy Markdown
Contributor Author

Thanks for your update :)

Thank you so much for reviewing and pointing out the weakness in the LRU cache :)

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

Erlend E. Aasland
Address review: visit node type for each strong ref
a15b49f
vstinner
vstinner reviewed May 27, 2021
View reviewed changes
Comment thread Modules/_functoolsmodule.c Outdated
Py_VISIT(pto->args);
Py_VISIT(pto->kw);
Py_VISIT(pto->dict);
Py_VISIT(Py_TYPE(pto));

@vstinner vstinner May 27, 2021

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@erlend-aasland erlend-aasland May 27, 2021

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I'll apply to all remaining PR's :)

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread Modules/_functoolsmodule.c
{Py_tp_members, partial_memberlist},
{Py_tp_getset, partial_getsetlist},
{Py_tp_new, partial_new},
{Py_tp_free, PyObject_GC_Del},

@vstinner vstinner May 27, 2021

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@erlend-aasland erlend-aasland May 27, 2021

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep!

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread Modules/_functoolsmodule.c Outdated
PyObject_Free(ko);
PyObject_GC_UnTrack(ko);
(void)keyobject_clear(ko);
PyObject_GC_Del(ko);

@vstinner vstinner May 27, 2021

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@erlend-aasland erlend-aasland May 27, 2021

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree :) Consistency FTW

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread Modules/_functoolsmodule.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Modules/_functoolsmodule.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Modules/_functoolsmodule.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

Erlend E. Aasland added 6 commits May 27, 2021 19:37
Erlend E. Aasland
Address review: visit members in correct order
0c8df1f
Erlend E. Aasland
Address review: revert Py_CLEAR change in lru_list_elem_dealloc
f9ac4c5
Erlend E. Aasland
Revert LRU cache element optimisation
fc39917
This reverts commit 6d7cdaf.
Erlend E. Aasland
Address review: revert redundant slot removal
07f2182
Erlend E. Aasland
Address review: use tp_free
0861788
Erlend E. Aasland
Revert lru list elem clear fn
7c2f750
@erlend-aasland

erlend-aasland commented May 27, 2021

Copy link
Copy Markdown
Contributor Author

@pablogsal, @shihai1991, @methane: I've reverted the LRU cache optimisations as per @vstinner's request.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

vstinner
vstinner reviewed May 27, 2021
View reviewed changes
Comment thread Modules/_functoolsmodule.c Outdated
Py_VISIT(link->result);
Py_VISIT(Py_TYPE(link));
return 0;
}

@vstinner vstinner May 27, 2021

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A lru_list_elem_clear() function would also be needed to implement the GC protocol, no?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@erlend-aasland erlend-aasland May 27, 2021

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@vstinner vstinner May 27, 2021

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@methane methane May 28, 2021

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_lru_list_elem is not a standalone type. It is an internal of _lru_cache_wrapper.

  • lru_cache_tp_traverse() visits lru_list_elem.
  • lru_cache_tp_clear() clears lru_list_elem.

That's why we can keep _lru_list_elem non-GC type.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@shihai1991 shihai1991 May 28, 2021

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO, Inada's #26416 is acceptable.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@vstinner

vstinner commented May 28, 2021

Copy link
Copy Markdown
Member

If you want to hide the functools._lru_list_elem type, the lru_cache_tp_traverse() function must not traverse its type. Otherwise, there is a risk that the type is discovered using gc.get_objects() or a similar function.

Right now, the functools._lru_list_elem type is hidden in the main branch and it doesn't implement the GC protocol. In this case, the current code for it in lru_cache_tp_traverse() in ok:

    lru_list_elem *link = self->root.next;
    while (link != &self->root) {
        lru_list_elem *next = link->next;
        Py_VISIT(link->key);
        Py_VISIT(link->result);
        link = next;
    }

And the current code for it in lru_cache_cache_clear() is also ok:

    lru_cache_clear_list(list);

But you still need to add Py_VISIT(Py_TYPE(self)); in lru_cache_tp_traverse().

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.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

Erlend E. Aasland
Revert "Revert LRU cache element optimisation"
6313247
This reverts commit fc39917.
@erlend-aasland

erlend-aasland commented May 28, 2021 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor Author

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.

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 lru_cache_tp_traverse to visit the LRU cache type and the LRU cache element type.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@methane

methane commented May 28, 2021

Copy link
Copy Markdown
Member

If you want to hide the functools._lru_list_elem type, the lru_cache_tp_traverse() function must not traverse its type. Otherwise, there is a risk that the type is discovered using gc.get_objects() or a similar function.

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.

Right now, the functools._lru_list_elem type is hidden in the main branch and it doesn't implement the GC protocol. In this case, the current code for it in lru_cache_tp_traverse() in ok:

Current PR breaks circular reference correctly. See #26363 (comment)

Since _lru_list_elem is hidden and immutable now, it is OK to remove Py_VISIT(Py_TYPE(link)); if we are sure about _lru_list_elem_type don't create circular reference.
But I am not sure about it. So I feel current PR is the best. It breaks circular reference correctly anyway.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@erlend-aasland

erlend-aasland commented May 28, 2021

Copy link
Copy Markdown
Contributor Author

Since _lru_list_elem is hidden and immutable now [...]

It's not immutable; only static types and heap types with the Py_TPFLAGS_IMMUTABLETYPE flag are immutable.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner

vstinner commented May 28, 2021

Copy link
Copy Markdown
Member

@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"):

static int
lru_cache_tp_traverse(lru_cache_object *self, visitproc visit, void *arg)
{
    ...
    lru_list_elem *link = self->root.next;
    while (link != &self->root) {
        lru_list_elem *next = link->next;
        Py_VISIT(link->key);
        Py_VISIT(link->result);
        Py_VISIT(Py_TYPE(link));  // <==== HERE
        link = next;
    }
    ...
}

Example:

$ cat x.py 
import functools

class Value:
    pass
value = Value()

@functools.lru_cache
def func():
    return value

func()

$ ./python -i x.py 
>>> import gc
>>> isinstance(int, type)
True
>>> types=[obj for obj in gc.get_objects() if isinstance(obj, type)]
>>> lru=[type for type in types if 'lru' in type.__qualname__]
>>> lru
[<class 'functools._lru_cache_wrapper'>, <class 'functools._lru_list_elem'>]

Here is the hidden functools._lru_list_elem type.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner

vstinner commented May 28, 2021

Copy link
Copy Markdown
Member

@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.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@erlend-aasland

erlend-aasland commented May 28, 2021 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor Author

@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.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

Erlend E. Aasland
Restore lru_cache_tp_traverse
1539195
@erlend-aasland
erlend-aasland requested a review from vstinner May 28, 2021 08:20
@erlend-aasland erlend-aasland changed the title bpo-42972: Fully implement GC protocol for functools types bpo-42972: Fully implement GC protocol for functools keywrapper and partial types May 28, 2021
vstinner
vstinner approved these changes May 28, 2021
View reviewed changes

@vstinner vstinner left a comment

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@bedevere-bot bedevere-bot added awaiting merge and removed awaiting core review labels May 28, 2021
@erlend-aasland

erlend-aasland commented May 28, 2021

Copy link
Copy Markdown
Contributor Author

@vstinner @methane, see GH-26423

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@erlend-aasland

erlend-aasland commented May 28, 2021

Copy link
Copy Markdown
Contributor Author

Thanks for your insights and reviews, everyone!

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner
vstinner merged commit 8994e9c into python:main May 28, 2021
@miss-islington

miss-islington commented May 28, 2021

Copy link
Copy Markdown
Contributor

Thanks @erlend-aasland for the PR, and @vstinner for merging it 🌮🎉.. I'm working now to backport this PR to: 3.10.
🐍🍒⛏🤖

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@bedevere-bot bedevere-bot removed the awaiting merge label May 28, 2021
@bedevere-bot

bedevere-bot commented May 28, 2021

Copy link
Copy Markdown

GH-26424 is a backport of this pull request to the 3.10 branch.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@bedevere-bot bedevere-bot removed the needs backport to 3.10 label May 28, 2021
miss-islington pushed a commit to miss-islington/cpython that referenced this pull request May 28, 2021
Erlend Egeberg Aasland @miss-islington
bpo-42972: Fully implement GC protocol for functools keywrapper and p…
491984e
…artial types (pythonGH-26363)

(cherry picked from commit 8994e9c)

Co-authored-by: Erlend Egeberg Aasland <[email protected]>
vstinner pushed a commit that referenced this pull request May 28, 2021
@miss-islington Erlend Egeberg Aasland
bpo-42972: Fully implement GC protocol for functools keywrapper and p…
eb8ab04
…artial types (GH-26363) (GH-26424)

(cherry picked from commit 8994e9c)

Co-authored-by: Erlend Egeberg Aasland <[email protected]>
@erlend-aasland
erlend-aasland deleted the bpo-42972/functools branch May 28, 2021 09:59
@pablogsal pablogsal mentioned this pull request Jul 18, 2021
[3.10] Correct the order of regen-abidump #27228
Closed
@vstinner vstinner mentioned this pull request Jun 9, 2022
[C API] Heap types (PyType_FromSpec) must fully implement the GC protocol #87138
Closed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@methane methane methane left review comments
@shihai1991 shihai1991 shihai1991 approved these changes
@vstinner vstinner vstinner approved these changes
@rhettinger rhettinger Awaiting requested review from rhettinger rhettinger is a code owner
@pablogsal pablogsal Awaiting requested review from pablogsal

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

skip news

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

8 participants

@erlend-aasland @vstinner @methane @miss-islington @bedevere-bot @shihai1991 @pablogsal @the-knights-who-say-ni
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.