gh-116868: Avoid locking in PyType_IsSubtype (#116829)

Make PyType_IsSubType not acquire lock
This commit is contained in:
Dino Viehland 2024-03-15 12:35:29 -04:00 committed by GitHub
parent ebf29b3a02
commit 280de3661b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 48 additions and 32 deletions

View File

@ -373,12 +373,23 @@ lookup_tp_mro(PyTypeObject *self)
PyObject * PyObject *
_PyType_GetMRO(PyTypeObject *self) _PyType_GetMRO(PyTypeObject *self)
{ {
PyObject *mro; #ifdef Py_GIL_DISABLED
PyObject *mro = _Py_atomic_load_ptr_relaxed(&self->tp_mro);
if (mro == NULL) {
return NULL;
}
if (_Py_TryIncref(&self->tp_mro, mro)) {
return mro;
}
BEGIN_TYPE_LOCK(); BEGIN_TYPE_LOCK();
mro = lookup_tp_mro(self); mro = lookup_tp_mro(self);
Py_INCREF(mro); Py_XINCREF(mro);
END_TYPE_LOCK() END_TYPE_LOCK()
return mro; return mro;
#else
return Py_XNewRef(lookup_tp_mro(self));
#endif
} }
static inline void static inline void
@ -911,7 +922,7 @@ PyType_Modified(PyTypeObject *type)
} }
static int static int
is_subtype_unlocked(PyTypeObject *a, PyTypeObject *b); is_subtype_with_mro(PyObject *a_mro, PyTypeObject *a, PyTypeObject *b);
static void static void
type_mro_modified(PyTypeObject *type, PyObject *bases) { type_mro_modified(PyTypeObject *type, PyObject *bases) {
@ -957,7 +968,7 @@ type_mro_modified(PyTypeObject *type, PyObject *bases) {
PyObject *b = PyTuple_GET_ITEM(bases, i); PyObject *b = PyTuple_GET_ITEM(bases, i);
PyTypeObject *cls = _PyType_CAST(b); PyTypeObject *cls = _PyType_CAST(b);
if (!is_subtype_unlocked(type, cls)) { if (!is_subtype_with_mro(lookup_tp_mro(type), type, cls)) {
goto clear; goto clear;
} }
} }
@ -1442,7 +1453,7 @@ type_set_bases_unlocked(PyTypeObject *type, PyObject *new_bases, void *context)
} }
PyTypeObject *base = (PyTypeObject*)ob; PyTypeObject *base = (PyTypeObject*)ob;
if (is_subtype_unlocked(base, type) || if (is_subtype_with_mro(lookup_tp_mro(base), base, type) ||
/* In case of reentering here again through a custom mro() /* In case of reentering here again through a custom mro()
the above check is not enough since it relies on the above check is not enough since it relies on
base->tp_mro which would gonna be updated inside base->tp_mro which would gonna be updated inside
@ -2303,37 +2314,41 @@ type_is_subtype_base_chain(PyTypeObject *a, PyTypeObject *b)
} }
static int static int
is_subtype_unlocked(PyTypeObject *a, PyTypeObject *b) is_subtype_with_mro(PyObject *a_mro, PyTypeObject *a, PyTypeObject *b)
{ {
PyObject *mro; int res;
if (a_mro != NULL) {
ASSERT_TYPE_LOCK_HELD();
mro = lookup_tp_mro(a);
if (mro != NULL) {
/* Deal with multiple inheritance without recursion /* Deal with multiple inheritance without recursion
by walking the MRO tuple */ by walking the MRO tuple */
Py_ssize_t i, n; Py_ssize_t i, n;
assert(PyTuple_Check(mro)); assert(PyTuple_Check(a_mro));
n = PyTuple_GET_SIZE(mro); n = PyTuple_GET_SIZE(a_mro);
res = 0;
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
if (PyTuple_GET_ITEM(mro, i) == (PyObject *)b) if (PyTuple_GET_ITEM(a_mro, i) == (PyObject *)b) {
return 1; res = 1;
break;
}
} }
return 0;
} }
else else {
/* a is not completely initialized yet; follow tp_base */ /* a is not completely initialized yet; follow tp_base */
return type_is_subtype_base_chain(a, b); res = type_is_subtype_base_chain(a, b);
}
return res;
} }
int int
PyType_IsSubtype(PyTypeObject *a, PyTypeObject *b) PyType_IsSubtype(PyTypeObject *a, PyTypeObject *b)
{ {
int res; #ifdef Py_GIL_DISABLED
BEGIN_TYPE_LOCK(); PyObject *mro = _PyType_GetMRO(a);
res = is_subtype_unlocked(a, b); int res = is_subtype_with_mro(mro, a, b);
END_TYPE_LOCK() Py_XDECREF(mro);
return res; return res;
#else
return is_subtype_with_mro(lookup_tp_mro(a), a, b);
#endif
} }
/* Routines to do a method lookup in the type without looking in the /* Routines to do a method lookup in the type without looking in the
@ -2826,7 +2841,7 @@ mro_check(PyTypeObject *type, PyObject *mro)
} }
PyTypeObject *base = (PyTypeObject*)obj; PyTypeObject *base = (PyTypeObject*)obj;
if (!is_subtype_unlocked(solid, solid_base(base))) { if (!is_subtype_with_mro(lookup_tp_mro(solid), solid, solid_base(base))) {
PyErr_Format( PyErr_Format(
PyExc_TypeError, PyExc_TypeError,
"mro() returned base with unsuitable layout ('%.500s')", "mro() returned base with unsuitable layout ('%.500s')",
@ -7082,28 +7097,29 @@ inherit_special(PyTypeObject *type, PyTypeObject *base)
#undef COPYVAL #undef COPYVAL
/* Setup fast subclass flags */ /* Setup fast subclass flags */
if (is_subtype_unlocked(base, (PyTypeObject*)PyExc_BaseException)) { PyObject *mro = lookup_tp_mro(base);
if (is_subtype_with_mro(mro, base, (PyTypeObject*)PyExc_BaseException)) {
type->tp_flags |= Py_TPFLAGS_BASE_EXC_SUBCLASS; type->tp_flags |= Py_TPFLAGS_BASE_EXC_SUBCLASS;
} }
else if (is_subtype_unlocked(base, &PyType_Type)) { else if (is_subtype_with_mro(mro, base, &PyType_Type)) {
type->tp_flags |= Py_TPFLAGS_TYPE_SUBCLASS; type->tp_flags |= Py_TPFLAGS_TYPE_SUBCLASS;
} }
else if (is_subtype_unlocked(base, &PyLong_Type)) { else if (is_subtype_with_mro(mro, base, &PyLong_Type)) {
type->tp_flags |= Py_TPFLAGS_LONG_SUBCLASS; type->tp_flags |= Py_TPFLAGS_LONG_SUBCLASS;
} }
else if (is_subtype_unlocked(base, &PyBytes_Type)) { else if (is_subtype_with_mro(mro, base, &PyBytes_Type)) {
type->tp_flags |= Py_TPFLAGS_BYTES_SUBCLASS; type->tp_flags |= Py_TPFLAGS_BYTES_SUBCLASS;
} }
else if (is_subtype_unlocked(base, &PyUnicode_Type)) { else if (is_subtype_with_mro(mro, base, &PyUnicode_Type)) {
type->tp_flags |= Py_TPFLAGS_UNICODE_SUBCLASS; type->tp_flags |= Py_TPFLAGS_UNICODE_SUBCLASS;
} }
else if (is_subtype_unlocked(base, &PyTuple_Type)) { else if (is_subtype_with_mro(mro, base, &PyTuple_Type)) {
type->tp_flags |= Py_TPFLAGS_TUPLE_SUBCLASS; type->tp_flags |= Py_TPFLAGS_TUPLE_SUBCLASS;
} }
else if (is_subtype_unlocked(base, &PyList_Type)) { else if (is_subtype_with_mro(mro, base, &PyList_Type)) {
type->tp_flags |= Py_TPFLAGS_LIST_SUBCLASS; type->tp_flags |= Py_TPFLAGS_LIST_SUBCLASS;
} }
else if (is_subtype_unlocked(base, &PyDict_Type)) { else if (is_subtype_with_mro(mro, base, &PyDict_Type)) {
type->tp_flags |= Py_TPFLAGS_DICT_SUBCLASS; type->tp_flags |= Py_TPFLAGS_DICT_SUBCLASS;
} }
@ -10204,7 +10220,7 @@ update_one_slot(PyTypeObject *type, pytype_slotdef *p)
d = (PyWrapperDescrObject *)descr; d = (PyWrapperDescrObject *)descr;
if ((specific == NULL || specific == d->d_wrapped) && if ((specific == NULL || specific == d->d_wrapped) &&
d->d_base->wrapper == p->wrapper && d->d_base->wrapper == p->wrapper &&
is_subtype_unlocked(type, PyDescr_TYPE(d))) is_subtype_with_mro(lookup_tp_mro(type), type, PyDescr_TYPE(d)))
{ {
specific = d->d_wrapped; specific = d->d_wrapped;
} }