Skip to content

Commit e4529ed

Browse files
committed
gh-145685: Update find_name_in_mro() to return a _PyStackRef
1 parent 1564e23 commit e4529ed

1 file changed

Lines changed: 35 additions & 45 deletions

File tree

Objects/typeobject.c

Lines changed: 35 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -5975,53 +5975,59 @@ PyObject_GetItemData(PyObject *obj)
59755975
}
59765976

59775977
/* Internal API to look for a name through the MRO, bypassing the method cache.
5978-
This returns a borrowed reference, and might set an exception.
5979-
'error' is set to: -1: error with exception; 1: error without exception; 0: ok */
5980-
static PyObject *
5981-
find_name_in_mro(PyTypeObject *type, PyObject *name, int *error)
5978+
The result is stored as a _PyStackRef in `out`. It never set an exception.
5979+
Returns -1 if there was an error, 0 if the name was not found, and 1 if
5980+
the name was found. */
5981+
static int
5982+
find_name_in_mro(PyTypeObject *type, PyObject *name, _PyStackRef *out)
59825983
{
59835984
Py_hash_t hash = _PyObject_HashFast(name);
59845985
if (hash == -1) {
5985-
*error = -1;
5986-
return NULL;
5986+
PyErr_Clear();
5987+
return -1;
59875988
}
59885989

59895990
/* Look in tp_dict of types in MRO */
59905991
PyObject *mro = lookup_tp_mro(type);
59915992
if (mro == NULL) {
59925993
if (!is_readying(type)) {
59935994
if (PyType_Ready(type) < 0) {
5994-
*error = -1;
5995-
return NULL;
5995+
PyErr_Clear();
5996+
return -1;
59965997
}
59975998
mro = lookup_tp_mro(type);
59985999
}
59996000
if (mro == NULL) {
6000-
*error = 1;
6001-
return NULL;
6001+
return -1;
60026002
}
60036003
}
60046004

6005-
PyObject *res = NULL;
6005+
int res = 0;
6006+
PyThreadState *tstate = _PyThreadState_GET();
60066007
/* Keep a strong reference to mro because type->tp_mro can be replaced
60076008
during dict lookup, e.g. when comparing to non-string keys. */
6008-
Py_INCREF(mro);
6009+
_PyCStackRef mro_ref;
6010+
_PyThreadState_PushCStackRef(tstate, &mro_ref);
6011+
mro_ref.ref = PyStackRef_FromPyObjectNew(mro);
60096012
Py_ssize_t n = PyTuple_GET_SIZE(mro);
60106013
for (Py_ssize_t i = 0; i < n; i++) {
60116014
PyObject *base = PyTuple_GET_ITEM(mro, i);
60126015
PyObject *dict = lookup_tp_dict(_PyType_CAST(base));
60136016
assert(dict && PyDict_Check(dict));
6014-
if (_PyDict_GetItemRef_KnownHash((PyDictObject *)dict, name, hash, &res) < 0) {
6015-
*error = -1;
6017+
Py_ssize_t ix = _Py_dict_lookup_threadsafe_stackref(
6018+
(PyDictObject *)dict, name, hash, out);
6019+
if (ix == DKIX_ERROR) {
6020+
PyErr_Clear();
6021+
res = -1;
60166022
goto done;
60176023
}
6018-
if (res != NULL) {
6024+
if (!PyStackRef_IsNull(*out)) {
6025+
res = 1;
60196026
break;
60206027
}
60216028
}
6022-
*error = 0;
60236029
done:
6024-
Py_DECREF(mro);
6030+
_PyThreadState_PopCStackRef(tstate, &mro_ref);
60256031
return res;
60266032
}
60276033

@@ -6176,11 +6182,11 @@ _PyType_LookupStackRefAndVersion(PyTypeObject *type, PyObject *name, _PyStackRef
61766182
// We need to atomically do the lookup and capture the version before
61776183
// anyone else can modify our mro or mutate the type.
61786184

6179-
PyObject *res;
6180-
int error;
6185+
int res;
61816186
PyInterpreterState *interp = _PyInterpreterState_GET();
61826187
int has_version = 0;
61836188
unsigned int assigned_version = 0;
6189+
61846190
BEGIN_TYPE_LOCK();
61856191
// We must assign the version before doing the lookup. If
61866192
// find_name_in_mro() blocks and releases the critical section
@@ -6189,35 +6195,24 @@ _PyType_LookupStackRefAndVersion(PyTypeObject *type, PyObject *name, _PyStackRef
61896195
has_version = assign_version_tag(interp, type);
61906196
assigned_version = type->tp_version_tag;
61916197
}
6192-
res = find_name_in_mro(type, name, &error);
6198+
res = find_name_in_mro(type, name, out);
61936199
END_TYPE_LOCK();
61946200

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

62126207
if (has_version) {
6208+
PyObject *res_obj = PyStackRef_AsPyObjectBorrow(*out);
62136209
#if Py_GIL_DISABLED
6214-
update_cache_gil_disabled(entry, name, assigned_version, res);
6210+
update_cache_gil_disabled(entry, name, assigned_version, res_obj);
62156211
#else
6216-
PyObject *old_value = update_cache(entry, name, assigned_version, res);
6212+
PyObject *old_value = update_cache(entry, name, assigned_version, res_obj);
62176213
Py_DECREF(old_value);
62186214
#endif
62196215
}
6220-
*out = res ? PyStackRef_FromPyObjectSteal(res) : PyStackRef_NULL;
62216216
return has_version ? assigned_version : 0;
62226217
}
62236218

@@ -11706,7 +11701,6 @@ update_one_slot(PyTypeObject *type, pytype_slotdef *p, pytype_slotdef **next_p,
1170611701
int use_generic = 0;
1170711702

1170811703
int offset = p->offset;
11709-
int error;
1171011704
void **ptr = slotptr(type, offset);
1171111705

1171211706
if (ptr == NULL) {
@@ -11722,19 +11716,15 @@ update_one_slot(PyTypeObject *type, pytype_slotdef *p, pytype_slotdef **next_p,
1172211716
assert(!PyErr_Occurred());
1172311717
do {
1172411718
/* Use faster uncached lookup as we won't get any cache hits during type setup. */
11725-
descr = find_name_in_mro(type, p->name_strobj, &error);
11726-
if (descr == NULL) {
11727-
if (error == -1) {
11728-
/* It is unlikely but not impossible that there has been an exception
11729-
during lookup. Since this function originally expected no errors,
11730-
we ignore them here in order to keep up the interface. */
11731-
PyErr_Clear();
11732-
}
11719+
_PyStackRef descr_ref;
11720+
int res = find_name_in_mro(type, p->name_strobj, &descr_ref);
11721+
if (res <= 0) {
1173311722
if (ptr == (void**)&type->tp_iternext) {
1173411723
specific = (void *)_PyObject_NextNotImplemented;
1173511724
}
1173611725
continue;
1173711726
}
11727+
descr = PyStackRef_AsPyObjectBorrow(descr_ref);
1173811728
if (Py_IS_TYPE(descr, &PyWrapperDescr_Type) &&
1173911729
((PyWrapperDescrObject *)descr)->d_base->name_strobj == p->name_strobj) {
1174011730
void **tptr;
@@ -11812,7 +11802,7 @@ update_one_slot(PyTypeObject *type, pytype_slotdef *p, pytype_slotdef **next_p,
1181211802
}
1181311803
}
1181411804
}
11815-
Py_DECREF(descr);
11805+
PyStackRef_CLOSE(descr_ref);
1181611806
} while ((++p)->offset == offset);
1181711807

1181811808
void *slot_value;

0 commit comments

Comments
 (0)