https://github.com/python/cpython/commit/6d28aaf24d8e6406944cf96995e7b34b4b625eea
commit: 6d28aaf24d8e6406944cf96995e7b34b4b625eea
branch: 3.14
author: Sam Gross <[email protected]>
committer: colesbury <[email protected]>
date: 2026-03-11T11:50:13Z
summary:
[3.14] gh-145685: Avoid contention on TYPE_LOCK in super() lookups (gh-145775)
(#145804)
(cherry picked from commit bdf6de8c3f0c2ec0d737f38014a32c1eed02c7f1)
files:
M Include/internal/pycore_stackref.h
M Objects/typeobject.c
M Tools/ftscalingbench/ftscalingbench.py
diff --git a/Include/internal/pycore_stackref.h
b/Include/internal/pycore_stackref.h
index 52acd918c9b9f9..76f6333739dfd6 100644
--- a/Include/internal/pycore_stackref.h
+++ b/Include/internal/pycore_stackref.h
@@ -735,6 +735,13 @@ _PyThreadState_PushCStackRef(PyThreadState *tstate,
_PyCStackRef *ref)
ref->ref = PyStackRef_NULL;
}
+static inline void
+_PyThreadState_PushCStackRefNew(PyThreadState *tstate, _PyCStackRef *ref,
PyObject *obj)
+{
+ _PyThreadState_PushCStackRef(tstate, ref);
+ ref->ref = PyStackRef_FromPyObjectNew(obj);
+}
+
static inline void
_PyThreadState_PopCStackRef(PyThreadState *tstate, _PyCStackRef *ref)
{
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index 232ead49a7f098..9777055c5f2313 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -11820,18 +11820,16 @@ _super_lookup_descr(PyTypeObject *su_type,
PyTypeObject *su_obj_type, PyObject *
PyObject *mro, *res;
Py_ssize_t i, n;
- BEGIN_TYPE_LOCK();
mro = lookup_tp_mro(su_obj_type);
- /* keep a strong reference to mro because su_obj_type->tp_mro can be
- replaced during PyDict_GetItemRef(dict, name, &res) and because
- another thread can modify it after we end the critical section
- below */
- Py_XINCREF(mro);
- END_TYPE_LOCK();
-
if (mro == NULL)
return NULL;
+ /* Keep a strong reference to mro because su_obj_type->tp_mro can be
+ replaced during PyDict_GetItemRef(dict, name, &res). */
+ PyThreadState *tstate = _PyThreadState_GET();
+ _PyCStackRef mro_ref;
+ _PyThreadState_PushCStackRefNew(tstate, &mro_ref, mro);
+
assert(PyTuple_Check(mro));
n = PyTuple_GET_SIZE(mro);
@@ -11842,7 +11840,7 @@ _super_lookup_descr(PyTypeObject *su_type, PyTypeObject
*su_obj_type, PyObject *
}
i++; /* skip su->type (if any) */
if (i >= n) {
- Py_DECREF(mro);
+ _PyThreadState_PopCStackRef(tstate, &mro_ref);
return NULL;
}
@@ -11853,13 +11851,13 @@ _super_lookup_descr(PyTypeObject *su_type,
PyTypeObject *su_obj_type, PyObject *
if (PyDict_GetItemRef(dict, name, &res) != 0) {
// found or error
- Py_DECREF(mro);
+ _PyThreadState_PopCStackRef(tstate, &mro_ref);
return res;
}
i++;
} while (i < n);
- Py_DECREF(mro);
+ _PyThreadState_PopCStackRef(tstate, &mro_ref);
return NULL;
}
diff --git a/Tools/ftscalingbench/ftscalingbench.py
b/Tools/ftscalingbench/ftscalingbench.py
index b815376b7ed56f..cc7d8575f5cfc9 100644
--- a/Tools/ftscalingbench/ftscalingbench.py
+++ b/Tools/ftscalingbench/ftscalingbench.py
@@ -201,6 +201,23 @@ def instantiate_dataclass():
for _ in range(1000 * WORK_SCALE):
obj = MyDataClass(x=1, y=2, z=3)
+@register_benchmark
+def super_call():
+ # TODO: super() on the same class from multiple threads still doesn't
+ # scale well, so use a class per-thread here for now.
+ class Base:
+ def method(self):
+ return 1
+
+ class Derived(Base):
+ def method(self):
+ return super().method()
+
+ obj = Derived()
+ for _ in range(1000 * WORK_SCALE):
+ obj.method()
+
+
def bench_one_thread(func):
t0 = time.perf_counter_ns()
func()
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]