Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

gh-131586: Avoid refcount contention in context managers#131851

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged

Conversation

colesbury
Copy link
Contributor

@colesburycolesbury commentedMar 28, 2025
edited by bedevere-appbot
Loading

This avoid reference count contention in the free threading build when calling special methods like__enter__ and__exit__.

This avoid reference count contention in the free threading buildwhen calling special methods like `__enter__` and `__exit__`.
inst(LOAD_SPECIAL, (owner -- attr, self_or_null)) {
assert(oparg <= SPECIAL_MAX);
PyObject *owner_o = PyStackRef_AsPyObjectSteal(owner);
op(_INSERT_NULL, (arg -- args[2])) {
Copy link
ContributorAuthor

Choose a reason for hiding this comment

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

@markshannon - this is what I was referring to in our last meeting. I had to split upLOAD_SPECIAL into two ops to ensure that thatframe->stackpointer includes both operands when it's spilled before the_PyObject_LookupSpecialMethod call.

I'm not sure if this is a better way to do this.

@@ -259,7 +259,7 @@ def pop(self, var: StackItem) -> tuple[str, Local]:
else:
rename = ""
if not popped.in_local:
assert popped.memory_offset is not None
#assert popped.memory_offset is not None
Copy link
ContributorAuthor

Choose a reason for hiding this comment

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

@markshannon - I'm not sure what to do about this.

@graingert
Copy link
Contributor

Oh woops I thought I was looking at a different pr!

colesbury reacted with thumbs up emoji

@markshannon
Copy link
Member

The simpler formulation

inst(LOAD_SPECIAL, (self--method_and_self[2])) {method_and_self[1]=self;method_and_self[0]=NULL;DEAD(self);PyObject*name=_Py_SpecialMethods[oparg].name;interr=_PyObject_LookupSpecialMethod(name,method_and_self);if (err<0) {if (!_PyErr_Occurred(tstate)) {_PyErr_Format(tstate,PyExc_TypeError,_Py_SpecialMethods[oparg].error,PyStackRef_TYPE(method_and_self[1])->tp_name);ERROR_NO_POP();                }ERROR_NO_POP();            }        }

should work now, with no changes to the cases generator needed.

Copy link
Member

@markshannonmarkshannon left a comment

Choose a reason for hiding this comment

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

Comment on lines 9271 to 9281
_PyStackRefself;
_PyStackRef*method_and_self;
self = stack_pointer[-1];
method_and_self =&stack_pointer[-1];
method_and_self[1] = self;
method_and_self[0] = PyStackRef_NULL;
PyObject *name = _Py_SpecialMethods[oparg].name;
PyObject *self_or_null_o;
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
_PyFrame_SetStackPointer(frame, stack_pointer);
PyObject *attr_o = _PyObject_LookupSpecialMethod(owner_o,name,&self_or_null_o);
int err = _PyObject_LookupSpecialMethod(name,method_and_self);
Copy link
ContributorAuthor

Choose a reason for hiding this comment

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

@markshannon - I switched to the simpler formulation, but the stackpointer adjustments aren't safe here for the free threaded build.

Thestack_pointer += -1; means thatself is not visible on the stack anymore when_PyObject_LookupSpecialMethod is called. In other words the code is generated as if the inputs are dead (okay), but the outputs are not yet alive (bad).

@python-cla-bot
Copy link

All commit authors signed the Contributor License Agreement.

CLA signed

Make sure that `method_and_self` stays alive across the call to_PyObject_LookupSpecialMethod. When written as a single op, the input ismarked as dead, but the outputs aren't considered alive until the end ofthe op.
@colesburycolesbury marked this pull request as ready for reviewApril 14, 2025 14:41
@colesbury
Copy link
ContributorAuthor

@markshannon@brandtbucher - the JIT build is failing on this PR. Would you please take a look? I think the generatedoptimizer_cases.c.h incorrectly manipulates the stack pointer.

@brandtbucher
Copy link
Member

Thanks for the ping, I’ll take a look at this tomorrow.

@brandtbucher
Copy link
Member

brandtbucher commentedApr 15, 2025
edited
Loading

Just from looking at the diff, Ithink the issue may be that we need a case inoptimizer_bytecodes.c for the new_INSERT_NULL instruction, that sets the output to a null symbol (usingsum_new_null, see_PUSH_NULL for an example). The default implementation that’s generated assumes all outputs are non-NULL, which obviously isn’t true here. I can confirm that this is the issue in a bit.

colesbury reacted with thumbs up emoji

@colesbury
Copy link
ContributorAuthor

Thanks@brandtbucher! Would you please take a look at my changes tooptimizer_bytecodes.c to see if they make sense?

Copy link
Member

@brandtbucherbrandtbucher left a comment

Choose a reason for hiding this comment

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

Yep, they look good.

colesbury reacted with thumbs up emoji
Copy link
Contributor

@mpagempage left a comment

Choose a reason for hiding this comment

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

LGTM

Comment on lines 2776 to 2780
// Lookup the method name `attr` on `self`. On entry, `method_and_self[0]`
// is null and `method_and_self[1]` is `self`. On exit, `method_and_self[0]`
// is the method object and `method_and_self[1]` is `self` if the method is
// not bound.
// Return 0 on success, -1 on error or if the method is missing.
Copy link
Contributor

Choose a reason for hiding this comment

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

This comment needs to be updated.

@markshannon
Copy link
Member

I don't see the need for arrays. Using arrays forces values into memory which prevents top of stack caching, and it makes the code harder to follow.

If you give the_PyObject_LookupSpecialMethod the signatureint _PyObject_LookupSpecialMethod(PyObject *owner, PyObject *name, _PyStackRef *method); and have it return -1 for error, 0 for normal attribute and 1 for method, then you can use the simpler implementation below. It is also necessary for_PyObject_LookupSpecialMethod tonot steal a reference toowner, but that's an easy change.

Using scalars works, as the code generator can track what is live or not and make sure that everything live is visible to the GC.
At least, it should do. Please file a bug report if it doesn't.

This does what you want, I think:

inst(LOAD_SPECIAL, (owner--attr,self_or_null)) {PyObject*owner_o=PyStackRef_AsPyObjectBorrow(owner);PyObject*name=_Py_SpecialMethods[oparg].name;// Returns -1 for error, 0 for normal attr, 1 for method.interr=_PyObject_LookupSpecialMethod(owner_o,name,&attr);if (err>0) {self_or_null=owner;DEAD(owner);            }elseif (err<0) {// The code generator assumes that _PyObject_LookupSpecialMethod// has defined `attr`, so overwrite it with `owner`.attr=owner;DEAD(owner);if (!_PyErr_Occurred(tstate)) {_PyErr_Format(tstate,PyExc_TypeError,_Py_SpecialMethods[oparg].error,Py_TYPE(owner_o)->tp_name);                }ERROR_IF(true,error);            }else {PyStackRef_CLOSE(owner);self_or_null=PyStackRef_NULL;            }        }

Copy link
Member

@markshannonmarkshannon left a comment

Choose a reason for hiding this comment

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

Try to avoid using arrays unless there is an actual array, like forBUILD_TUPLE.

Also, if the code generator is not doing what you think it should, file a bug report.
Either we need to document it, or fix it.

@bedevere-app
Copy link

When you're done making the requested changes, leave the comment:I have made the requested changes; please review again.

@colesbury
Copy link
ContributorAuthor

No, that doesn't work@markshannon. Bothowner and the looked up method need to be on the stack and visible to the GC.

In your formulation, once_PyObject_LookupSpecialMethod() writes the method to&attr, it will overwrite the stack slot containing owner. That's a problem because the GC will not seeowner in a subsequent escaping call (such as thetp_descr_get call in_PyObject_LookupSpecialMethod).

@colesbury
Copy link
ContributorAuthor

colesbury commentedApr 16, 2025
edited
Loading

Here is part of the generated code for your formulation._PyObject_LookupSpecialMethod() needs to store the looked up method/attribute in a place that's visible to the GC. The&attr is not visible to the GC because it's just a local C variable.

_PyStackRefowner;_PyStackRefattr;_PyStackRefself_or_null;owner=stack_pointer[-1];PyObject*owner_o=PyStackRef_AsPyObjectBorrow(owner);PyObject*name=_Py_SpecialMethods[oparg].name;_PyFrame_SetStackPointer(frame,stack_pointer);interr=_PyObject_LookupSpecialMethod(owner_o,name,&attr);

@markshannon
Copy link
Member

attr is not defined when_PyObject_LookupSpecialMethod is called, so it would be bad if the GC could see it.

@colesbury
Copy link
ContributorAuthor

That's why it's initialized toPyStackRef_NULL in this PR, but you can't do that with your formulation.

@colesbury
Copy link
ContributorAuthor

No difference in single-threaded performance (<0.02% default; <0.06% FT; clang-20 tail-call)

@colesburycolesbury added the 🔨 test-with-refleak-buildbotsTest PR w/ refleak buildbots; report in status section labelApr 21, 2025
@bedevere-bot
Copy link

🤖 New build scheduled with the buildbot fleet by@colesbury for commit270b87b 🤖

Results will be shown at:

https://buildbot.python.org/all/#/grid?branch=refs%2Fpull%2F131851%2Fmerge

If you want to schedule another build, you need to add the🔨 test-with-refleak-buildbots label again.

@bedevere-botbedevere-bot removed the 🔨 test-with-refleak-buildbotsTest PR w/ refleak buildbots; report in status section labelApr 21, 2025
@colesburycolesbury dismissedmarkshannon’sstale reviewApril 21, 2025 19:52

Discussed in Faster CPython Sync

@colesburycolesbury merged commitda53660 intopython:mainApr 21, 2025
76 of 81 checks passed
@colesburycolesbury deleted the gh-131586-lookup-special-extra branchApril 21, 2025 19:54
@bedevere-bot
Copy link

⚠️⚠️⚠️ Buildbot failure⚠️⚠️⚠️

Hi! The buildbotaarch64 Fedora Stable Clang 3.x (tier-2) has failed when building commitda53660.

What do you need to do:

  1. Don't panic.
  2. Checkthe buildbot page in the devguide if you don't know what the buildbots are or how they work.
  3. Go to the page of the buildbot that failed (https://buildbot.python.org/#/builders/234/builds/7453) and take a look at the build logs.
  4. Check if the failure is related to this commit (da53660) or if it is a false positive.
  5. If the failure is related to this commit, please, reflect that on the issue and make a new Pull Request with a fix.

You can take a look at the buildbot page here:

https://buildbot.python.org/#/builders/234/builds/7453

Failed tests:

  • test_interpreters

Failed subtests:

  • test_python_calls_do_not_appear_in_the_stack_if_perf_deactivated - test.test_perf_profiler.TestPerfProfilerWithDwarf.test_python_calls_do_not_appear_in_the_stack_if_perf_deactivated

Summary of the results of the build (if available):

==

Click to see traceback logs
Traceback (most recent call last):  File"/home/buildbot/buildarea/3.x.cstratak-fedora-stable-aarch64.clang/build/Lib/threading.py", line1079, in_bootstrap_innerself._context.run(self.run)~~~~~~~~~~~~~~~~~^^^^^^^^^^  File"/home/buildbot/buildarea/3.x.cstratak-fedora-stable-aarch64.clang/build/Lib/threading.py", line1021, inrunself._target(*self._args,**self._kwargs)~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^  File"/home/buildbot/buildarea/3.x.cstratak-fedora-stable-aarch64.clang/build/Lib/test/test_interpreters/test_stress.py", line30, intask    interp= interpreters.create()  File"/home/buildbot/buildarea/3.x.cstratak-fedora-stable-aarch64.clang/build/Lib/test/support/interpreters/__init__.py", line76, increateid= _interpreters.create(reqrefs=True)interpreters.InterpreterError:interpreter creation failedkTraceback (most recent call last):  File"/home/buildbot/buildarea/3.x.cstratak-fedora-stable-aarch64.clang/build/Lib/threading.py", line1079, in_bootstrap_innerself._context.run(self.run)~~~~~~~~~~~~~~~~~^^^^^^^^^^  File"/home/buildbot/buildarea/3.x.cstratak-fedora-stable-aarch64.clang/build/Lib/threading.py", line1021, inrunself._target(*self._args,**self._kwargs)~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^  File"/home/buildbot/buildarea/3.x.cstratak-fedora-stable-aarch64.clang/build/Lib/test/test_interpreters/test_stress.py", line47, inrun    interp= interpreters.create()  File"/home/buildbot/buildarea/3.x.cstratak-fedora-stable-aarch64.clang/build/Lib/test/support/interpreters/__init__.py", line76, increateid= _interpreters.create(reqrefs=True)interpreters.InterpreterError:interpreter creation failedkTraceback (most recent call last):  File"/home/buildbot/buildarea/3.x.cstratak-fedora-stable-aarch64.clang/build/Lib/test/test_perf_profiler.py", line401, intest_python_calls_do_not_appear_in_the_stack_if_perf_deactivatedself.assertEqual(stderr,"")~~~~~~~~~~~~~~~~^^^^^^^^^^^^AssertionError:'Warning:\nProcessed 259 events and lost 1[34 chars]\n\n' != ''- Warning:- Processed 259 events and lost 1 chunks!- - Check IO/CPU overload!-

Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment
Reviewers

@mpagempagempage approved these changes

@brandtbucherbrandtbucherbrandtbucher approved these changes

@markshannonmarkshannonmarkshannon left review comments

@Fidget-SpinnerFidget-SpinnerAwaiting requested review from Fidget-SpinnerFidget-Spinner is a code owner

Assignees
No one assigned
Projects
None yet
Milestone
No milestone
Development

Successfully merging this pull request may close these issues.

6 participants
@colesbury@graingert@markshannon@brandtbucher@bedevere-bot@mpage

[8]ページ先頭

©2009-2025 Movatter.jp