Skip to content

Commit f26eca7

Browse files
authored
gh-145685: Update find_name_in_mro() to return a _PyStackRef (gh-145693)
1 parent c4333a1 commit f26eca7

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
@@ -5978,53 +5978,59 @@ PyObject_GetItemData(PyObject *obj)
59785978
}
59795979

59805980
/* Internal API to look for a name through the MRO, bypassing the method cache.
5981-
This returns a borrowed reference, and might set an exception.
5982-
'error' is set to: -1: error with exception; 1: error without exception; 0: ok */
5983-
static PyObject *
5984-
find_name_in_mro(PyTypeObject *type, PyObject *name, int *error)
5981+
The result is stored as a _PyStackRef in `out`. It never set an exception.
5982+
Returns -1 if there was an error, 0 if the name was not found, and 1 if
5983+
the name was found. */
5984+
static int
5985+
find_name_in_mro(PyTypeObject *type, PyObject *name, _PyStackRef *out)
59855986
{
59865987
Py_hash_t hash = _PyObject_HashFast(name);
59875988
if (hash == -1) {
5988-
*error = -1;
5989-
return NULL;
5989+
PyErr_Clear();
5990+
return -1;
59905991
}
59915992

59925993
/* Look in tp_dict of types in MRO */
59935994
PyObject *mro = lookup_tp_mro(type);
59945995
if (mro == NULL) {
59955996
if (!is_readying(type)) {
59965997
if (PyType_Ready(type) < 0) {
5997-
*error = -1;
5998-
return NULL;
5998+
PyErr_Clear();
5999+
return -1;
59996000
}
60006001
mro = lookup_tp_mro(type);
60016002
}
60026003
if (mro == NULL) {
6003-
*error = 1;
6004-
return NULL;
6004+
return -1;
60056005
}
60066006
}
60076007

6008-
PyObject *res = NULL;
6008+
int res = 0;
6009+
PyThreadState *tstate = _PyThreadState_GET();
60096010
/* Keep a strong reference to mro because type->tp_mro can be replaced
60106011
during dict lookup, e.g. when comparing to non-string keys. */
6011-
Py_INCREF(mro);
6012+
_PyCStackRef mro_ref;
6013+
_PyThreadState_PushCStackRef(tstate, &mro_ref);
6014+
mro_ref.ref = PyStackRef_FromPyObjectNew(mro);
60126015
Py_ssize_t n = PyTuple_GET_SIZE(mro);
60136016
for (Py_ssize_t i = 0; i < n; i++) {
60146017
PyObject *base = PyTuple_GET_ITEM(mro, i);
60156018
PyObject *dict = lookup_tp_dict(_PyType_CAST(base));
60166019
assert(dict && PyDict_Check(dict));
6017-
if (_PyDict_GetItemRef_KnownHash((PyDictObject *)dict, name, hash, &res) < 0) {
6018-
*error = -1;
6020+
Py_ssize_t ix = _Py_dict_lookup_threadsafe_stackref(
6021+
(PyDictObject *)dict, name, hash, out);
6022+
if (ix == DKIX_ERROR) {
6023+
PyErr_Clear();
6024+
res = -1;
60196025
goto done;
60206026
}
6021-
if (res != NULL) {
6027+
if (!PyStackRef_IsNull(*out)) {
6028+
res = 1;
60226029
break;
60236030
}
60246031
}
6025-
*error = 0;
60266032
done:
6027-
Py_DECREF(mro);
6033+
_PyThreadState_PopCStackRef(tstate, &mro_ref);
60286034
return res;
60296035
}
60306036

@@ -6179,11 +6185,11 @@ _PyType_LookupStackRefAndVersion(PyTypeObject *type, PyObject *name, _PyStackRef
61796185
// We need to atomically do the lookup and capture the version before
61806186
// anyone else can modify our mro or mutate the type.
61816187

6182-
PyObject *res;
6183-
int error;
6188+
int res;
61846189
PyInterpreterState *interp = _PyInterpreterState_GET();
61856190
int has_version = 0;
61866191
unsigned int assigned_version = 0;
6192+
61876193
BEGIN_TYPE_LOCK();
61886194
// We must assign the version before doing the lookup. If
61896195
// find_name_in_mro() blocks and releases the critical section
@@ -6192,35 +6198,24 @@ _PyType_LookupStackRefAndVersion(PyTypeObject *type, PyObject *name, _PyStackRef
61926198
has_version = assign_version_tag(interp, type);
61936199
assigned_version = type->tp_version_tag;
61946200
}
6195-
res = find_name_in_mro(type, name, &error);
6201+
res = find_name_in_mro(type, name, out);
61966202
END_TYPE_LOCK();
61976203

61986204
/* Only put NULL results into cache if there was no error. */
6199-
if (error) {
6200-
/* It's not ideal to clear the error condition,
6201-
but this function is documented as not setting
6202-
an exception, and I don't want to change that.
6203-
E.g., when PyType_Ready() can't proceed, it won't
6204-
set the "ready" flag, so future attempts to ready
6205-
the same type will call it again -- hopefully
6206-
in a context that propagates the exception out.
6207-
*/
6208-
if (error == -1) {
6209-
PyErr_Clear();
6210-
}
6205+
if (res < 0) {
62116206
*out = PyStackRef_NULL;
62126207
return 0;
62136208
}
62146209

62156210
if (has_version) {
6211+
PyObject *res_obj = PyStackRef_AsPyObjectBorrow(*out);
62166212
#if Py_GIL_DISABLED
6217-
update_cache_gil_disabled(entry, name, assigned_version, res);
6213+
update_cache_gil_disabled(entry, name, assigned_version, res_obj);
62186214
#else
6219-
PyObject *old_value = update_cache(entry, name, assigned_version, res);
6215+
PyObject *old_value = update_cache(entry, name, assigned_version, res_obj);
62206216
Py_DECREF(old_value);
62216217
#endif
62226218
}
6223-
*out = res ? PyStackRef_FromPyObjectSteal(res) : PyStackRef_NULL;
62246219
return has_version ? assigned_version : 0;
62256220
}
62266221

@@ -11709,7 +11704,6 @@ update_one_slot(PyTypeObject *type, pytype_slotdef *p, pytype_slotdef **next_p,
1170911704
int use_generic = 0;
1171011705

1171111706
int offset = p->offset;
11712-
int error;
1171311707
void **ptr = slotptr(type, offset);
1171411708

1171511709
if (ptr == NULL) {
@@ -11725,19 +11719,15 @@ update_one_slot(PyTypeObject *type, pytype_slotdef *p, pytype_slotdef **next_p,
1172511719
assert(!PyErr_Occurred());
1172611720
do {
1172711721
/* Use faster uncached lookup as we won't get any cache hits during type setup. */
11728-
descr = find_name_in_mro(type, p->name_strobj, &error);
11729-
if (descr == NULL) {
11730-
if (error == -1) {
11731-
/* It is unlikely but not impossible that there has been an exception
11732-
during lookup. Since this function originally expected no errors,
11733-
we ignore them here in order to keep up the interface. */
11734-
PyErr_Clear();
11735-
}
11722+
_PyStackRef descr_ref;
11723+
int res = find_name_in_mro(type, p->name_strobj, &descr_ref);
11724+
if (res <= 0) {
1173611725
if (ptr == (void**)&type->tp_iternext) {
1173711726
specific = (void *)_PyObject_NextNotImplemented;
1173811727
}
1173911728
continue;
1174011729
}
11730+
descr = PyStackRef_AsPyObjectBorrow(descr_ref);
1174111731
if (Py_IS_TYPE(descr, &PyWrapperDescr_Type) &&
1174211732
((PyWrapperDescrObject *)descr)->d_base->name_strobj == p->name_strobj) {
1174311733
void **tptr;
@@ -11815,7 +11805,7 @@ update_one_slot(PyTypeObject *type, pytype_slotdef *p, pytype_slotdef **next_p,
1181511805
}
1181611806
}
1181711807
}
11818-
Py_DECREF(descr);
11808+
PyStackRef_CLOSE(descr_ref);
1181911809
} while ((++p)->offset == offset);
1182011810

1182111811
void *slot_value;

0 commit comments

Comments
 (0)