Skip to content

Commit e7bc152

Browse files
[3.14] gh-145685: Update find_name_in_mro() to return a _PyStackRef (gh-145693) (#145769)
(cherry picked from commit f26eca7) Co-authored-by: Sam Gross <colesbury@gmail.com>
1 parent e12cc26 commit e7bc152

File tree

1 file changed

+35
-45
lines changed

1 file changed

+35
-45
lines changed

Objects/typeobject.c

Lines changed: 35 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -5702,55 +5702,61 @@ PyObject_GetItemData(PyObject *obj)
57025702
}
57035703

57045704
/* Internal API to look for a name through the MRO, bypassing the method cache.
5705-
This returns a borrowed reference, and might set an exception.
5706-
'error' is set to: -1: error with exception; 1: error without exception; 0: ok */
5707-
static PyObject *
5708-
find_name_in_mro(PyTypeObject *type, PyObject *name, int *error)
5705+
The result is stored as a _PyStackRef in `out`. It never set an exception.
5706+
Returns -1 if there was an error, 0 if the name was not found, and 1 if
5707+
the name was found. */
5708+
static int
5709+
find_name_in_mro(PyTypeObject *type, PyObject *name, _PyStackRef *out)
57095710
{
57105711
ASSERT_TYPE_LOCK_HELD();
57115712

57125713
Py_hash_t hash = _PyObject_HashFast(name);
57135714
if (hash == -1) {
5714-
*error = -1;
5715-
return NULL;
5715+
PyErr_Clear();
5716+
return -1;
57165717
}
57175718

57185719
/* Look in tp_dict of types in MRO */
57195720
PyObject *mro = lookup_tp_mro(type);
57205721
if (mro == NULL) {
57215722
if (!is_readying(type)) {
57225723
if (PyType_Ready(type) < 0) {
5723-
*error = -1;
5724-
return NULL;
5724+
PyErr_Clear();
5725+
return -1;
57255726
}
57265727
mro = lookup_tp_mro(type);
57275728
}
57285729
if (mro == NULL) {
5729-
*error = 1;
5730-
return NULL;
5730+
return -1;
57315731
}
57325732
}
57335733

5734-
PyObject *res = NULL;
5734+
int res = 0;
5735+
PyThreadState *tstate = _PyThreadState_GET();
57355736
/* Keep a strong reference to mro because type->tp_mro can be replaced
57365737
during dict lookup, e.g. when comparing to non-string keys. */
5737-
Py_INCREF(mro);
5738+
_PyCStackRef mro_ref;
5739+
_PyThreadState_PushCStackRef(tstate, &mro_ref);
5740+
mro_ref.ref = PyStackRef_FromPyObjectNew(mro);
57385741
Py_ssize_t n = PyTuple_GET_SIZE(mro);
57395742
for (Py_ssize_t i = 0; i < n; i++) {
57405743
PyObject *base = PyTuple_GET_ITEM(mro, i);
57415744
PyObject *dict = lookup_tp_dict(_PyType_CAST(base));
57425745
assert(dict && PyDict_Check(dict));
5743-
if (_PyDict_GetItemRef_KnownHash((PyDictObject *)dict, name, hash, &res) < 0) {
5744-
*error = -1;
5746+
Py_ssize_t ix = _Py_dict_lookup_threadsafe_stackref(
5747+
(PyDictObject *)dict, name, hash, out);
5748+
if (ix == DKIX_ERROR) {
5749+
PyErr_Clear();
5750+
res = -1;
57455751
goto done;
57465752
}
5747-
if (res != NULL) {
5753+
if (!PyStackRef_IsNull(*out)) {
5754+
res = 1;
57485755
break;
57495756
}
57505757
}
5751-
*error = 0;
57525758
done:
5753-
Py_DECREF(mro);
5759+
_PyThreadState_PopCStackRef(tstate, &mro_ref);
57545760
return res;
57555761
}
57565762

@@ -5905,11 +5911,11 @@ _PyType_LookupStackRefAndVersion(PyTypeObject *type, PyObject *name, _PyStackRef
59055911
// We need to atomically do the lookup and capture the version before
59065912
// anyone else can modify our mro or mutate the type.
59075913

5908-
PyObject *res;
5909-
int error;
5914+
int res;
59105915
PyInterpreterState *interp = _PyInterpreterState_GET();
59115916
int has_version = 0;
59125917
unsigned int assigned_version = 0;
5918+
59135919
BEGIN_TYPE_LOCK();
59145920
// We must assign the version before doing the lookup. If
59155921
// find_name_in_mro() blocks and releases the critical section
@@ -5918,35 +5924,24 @@ _PyType_LookupStackRefAndVersion(PyTypeObject *type, PyObject *name, _PyStackRef
59185924
has_version = assign_version_tag(interp, type);
59195925
assigned_version = type->tp_version_tag;
59205926
}
5921-
res = find_name_in_mro(type, name, &error);
5927+
res = find_name_in_mro(type, name, out);
59225928
END_TYPE_LOCK();
59235929

59245930
/* Only put NULL results into cache if there was no error. */
5925-
if (error) {
5926-
/* It's not ideal to clear the error condition,
5927-
but this function is documented as not setting
5928-
an exception, and I don't want to change that.
5929-
E.g., when PyType_Ready() can't proceed, it won't
5930-
set the "ready" flag, so future attempts to ready
5931-
the same type will call it again -- hopefully
5932-
in a context that propagates the exception out.
5933-
*/
5934-
if (error == -1) {
5935-
PyErr_Clear();
5936-
}
5931+
if (res < 0) {
59375932
*out = PyStackRef_NULL;
59385933
return 0;
59395934
}
59405935

59415936
if (has_version) {
5937+
PyObject *res_obj = PyStackRef_AsPyObjectBorrow(*out);
59425938
#if Py_GIL_DISABLED
5943-
update_cache_gil_disabled(entry, name, assigned_version, res);
5939+
update_cache_gil_disabled(entry, name, assigned_version, res_obj);
59445940
#else
5945-
PyObject *old_value = update_cache(entry, name, assigned_version, res);
5941+
PyObject *old_value = update_cache(entry, name, assigned_version, res_obj);
59465942
Py_DECREF(old_value);
59475943
#endif
59485944
}
5949-
*out = res ? PyStackRef_FromPyObjectSteal(res) : PyStackRef_NULL;
59505945
return has_version ? assigned_version : 0;
59515946
}
59525947

@@ -11306,7 +11301,6 @@ update_one_slot(PyTypeObject *type, pytype_slotdef *p)
1130611301
int use_generic = 0;
1130711302

1130811303
int offset = p->offset;
11309-
int error;
1131011304
void **ptr = slotptr(type, offset);
1131111305

1131211306
if (ptr == NULL) {
@@ -11319,19 +11313,15 @@ update_one_slot(PyTypeObject *type, pytype_slotdef *p)
1131911313
assert(!PyErr_Occurred());
1132011314
do {
1132111315
/* Use faster uncached lookup as we won't get any cache hits during type setup. */
11322-
descr = find_name_in_mro(type, p->name_strobj, &error);
11323-
if (descr == NULL) {
11324-
if (error == -1) {
11325-
/* It is unlikely but not impossible that there has been an exception
11326-
during lookup. Since this function originally expected no errors,
11327-
we ignore them here in order to keep up the interface. */
11328-
PyErr_Clear();
11329-
}
11316+
_PyStackRef descr_ref;
11317+
int res = find_name_in_mro(type, p->name_strobj, &descr_ref);
11318+
if (res <= 0) {
1133011319
if (ptr == (void**)&type->tp_iternext) {
1133111320
specific = (void *)_PyObject_NextNotImplemented;
1133211321
}
1133311322
continue;
1133411323
}
11324+
descr = PyStackRef_AsPyObjectBorrow(descr_ref);
1133511325
if (Py_IS_TYPE(descr, &PyWrapperDescr_Type) &&
1133611326
((PyWrapperDescrObject *)descr)->d_base->name_strobj == p->name_strobj) {
1133711327
void **tptr = resolve_slotdups(type, p->name_strobj);
@@ -11399,7 +11389,7 @@ update_one_slot(PyTypeObject *type, pytype_slotdef *p)
1139911389
type_clear_flags(type, Py_TPFLAGS_HAVE_VECTORCALL);
1140011390
}
1140111391
}
11402-
Py_DECREF(descr);
11392+
PyStackRef_CLOSE(descr_ref);
1140311393
} while ((++p)->offset == offset);
1140411394
if (specific && !use_generic)
1140511395
*ptr = specific;

0 commit comments

Comments
 (0)