gh-131586: Avoid refcount contention in some "special" calls (#131588)

In the free threaded build, the `_PyObject_LookupSpecial()` call can lead to
reference count contention on the returned function object becuase it
doesn't use stackrefs. Refactor some of the callers to use
`_PyObject_MaybeCallSpecialNoArgs`, which uses stackrefs internally.

This fixes the scaling bottleneck in the "lookup_special" microbenchmark
in `ftscalingbench.py`. However, the are still some uses of
`_PyObject_LookupSpecial()` that need to be addressed in future PRs.
This commit is contained in:
Sam Gross 2025-03-26 14:38:47 -04:00 committed by GitHub
parent 3d4ac1a2c2
commit 67fbfb42bd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 450 additions and 374 deletions

View file

@ -2537,22 +2537,19 @@ static PyObject *
builtin_round_impl(PyObject *module, PyObject *number, PyObject *ndigits)
/*[clinic end generated code: output=ff0d9dd176c02ede input=275678471d7aca15]*/
{
PyObject *round, *result;
round = _PyObject_LookupSpecial(number, &_Py_ID(__round__));
if (round == NULL) {
if (!PyErr_Occurred())
PyErr_Format(PyExc_TypeError,
"type %.100s doesn't define __round__ method",
Py_TYPE(number)->tp_name);
return NULL;
PyObject *result;
if (ndigits == Py_None) {
result = _PyObject_MaybeCallSpecialNoArgs(number, &_Py_ID(__round__));
}
else {
result = _PyObject_MaybeCallSpecialOneArg(number, &_Py_ID(__round__),
ndigits);
}
if (result == NULL && !PyErr_Occurred()) {
PyErr_Format(PyExc_TypeError,
"type %.100s doesn't define __round__ method",
Py_TYPE(number)->tp_name);
}
if (ndigits == Py_None)
result = _PyObject_CallNoArgs(round);
else
result = PyObject_CallOneArg(round, ndigits);
Py_DECREF(round);
return result;
}

View file

@ -433,6 +433,12 @@ static void
gc_visit_thread_stacks(PyInterpreterState *interp, struct collection_state *state)
{
_Py_FOR_EACH_TSTATE_BEGIN(interp, p) {
_PyCStackRef *c_ref = ((_PyThreadStateImpl *)p)->c_stack_refs;
while (c_ref != NULL) {
gc_visit_stackref(c_ref->ref);
c_ref = c_ref->next;
}
for (_PyInterpreterFrame *f = p->current_frame; f != NULL; f = f->previous) {
if (f->owner >= FRAME_OWNED_BY_INTERPRETER) {
continue;