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

@ -1680,14 +1680,20 @@ _PyObject_GenericGetAttrWithDict(PyObject *obj, PyObject *name,
Py_TYPE(name)->tp_name);
return NULL;
}
Py_INCREF(name);
if (!_PyType_IsReady(tp)) {
if (PyType_Ready(tp) < 0)
goto done;
return NULL;
}
descr = _PyType_LookupRef(tp, name);
Py_INCREF(name);
PyThreadState *tstate = _PyThreadState_GET();
_PyCStackRef cref;
_PyThreadState_PushCStackRef(tstate, &cref);
_PyType_LookupStackRefAndVersion(tp, name, &cref.ref);
descr = PyStackRef_AsPyObjectBorrow(cref.ref);
f = NULL;
if (descr != NULL) {
@ -1758,8 +1764,8 @@ _PyObject_GenericGetAttrWithDict(PyObject *obj, PyObject *name,
}
if (descr != NULL) {
res = descr;
descr = NULL;
res = PyStackRef_AsPyObjectSteal(cref.ref);
cref.ref = PyStackRef_NULL;
goto done;
}
@ -1771,7 +1777,7 @@ _PyObject_GenericGetAttrWithDict(PyObject *obj, PyObject *name,
_PyObject_SetAttributeErrorContext(obj, name);
}
done:
Py_XDECREF(descr);
_PyThreadState_PopCStackRef(tstate, &cref);
Py_DECREF(name);
return res;
}
@ -1805,7 +1811,13 @@ _PyObject_GenericSetAttrWithDict(PyObject *obj, PyObject *name,
Py_INCREF(name);
Py_INCREF(tp);
descr = _PyType_LookupRef(tp, name);
PyThreadState *tstate = _PyThreadState_GET();
_PyCStackRef cref;
_PyThreadState_PushCStackRef(tstate, &cref);
_PyType_LookupStackRefAndVersion(tp, name, &cref.ref);
descr = PyStackRef_AsPyObjectBorrow(cref.ref);
if (descr != NULL) {
f = Py_TYPE(descr)->tp_descr_set;
@ -1872,7 +1884,7 @@ _PyObject_GenericSetAttrWithDict(PyObject *obj, PyObject *name,
_PyObject_SetAttributeErrorContext(obj, name);
}
done:
Py_XDECREF(descr);
_PyThreadState_PopCStackRef(tstate, &cref);
Py_DECREF(tp);
Py_DECREF(name);
return res;