gh-134043: use stackrefs for dict lookup in _PyObject_GetMethodStackRef (#136412)

Co-authored-by: Sam Gross <colesbury@gmail.com>
This commit is contained in:
Kumar Aditya 2025-07-28 22:04:57 +05:30 committed by GitHub
parent 1e9b8f2f85
commit cbe6ebe15b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 90 additions and 31 deletions

View file

@ -113,6 +113,8 @@ extern Py_ssize_t _Py_dict_lookup(PyDictObject *mp, PyObject *key, Py_hash_t has
extern Py_ssize_t _Py_dict_lookup_threadsafe(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr); extern Py_ssize_t _Py_dict_lookup_threadsafe(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr);
extern Py_ssize_t _Py_dict_lookup_threadsafe_stackref(PyDictObject *mp, PyObject *key, Py_hash_t hash, _PyStackRef *value_addr); extern Py_ssize_t _Py_dict_lookup_threadsafe_stackref(PyDictObject *mp, PyObject *key, Py_hash_t hash, _PyStackRef *value_addr);
extern int _PyDict_GetMethodStackRef(PyDictObject *dict, PyObject *name, _PyStackRef *method);
extern Py_ssize_t _PyDict_LookupIndex(PyDictObject *, PyObject *); extern Py_ssize_t _PyDict_LookupIndex(PyDictObject *, PyObject *);
extern Py_ssize_t _PyDictKeys_StringLookup(PyDictKeysObject* dictkeys, PyObject *key); extern Py_ssize_t _PyDictKeys_StringLookup(PyDictKeysObject* dictkeys, PyObject *key);

View file

@ -839,6 +839,13 @@ _Py_TryXGetStackRef(PyObject **src, _PyStackRef *out)
#endif #endif
#define PyStackRef_XSETREF(dst, src) \
do { \
_PyStackRef _tmp_dst_ref = (dst); \
(dst) = (src); \
PyStackRef_XCLOSE(_tmp_dst_ref); \
} while(0)
// Like Py_VISIT but for _PyStackRef fields // Like Py_VISIT but for _PyStackRef fields
#define _Py_VISIT_STACKREF(ref) \ #define _Py_VISIT_STACKREF(ref) \
do { \ do { \

View file

@ -1581,11 +1581,12 @@ _Py_dict_lookup_threadsafe(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyOb
return ix; return ix;
} }
Py_ssize_t static Py_ssize_t
_Py_dict_lookup_threadsafe_stackref(PyDictObject *mp, PyObject *key, Py_hash_t hash, _PyStackRef *value_addr) lookup_threadsafe_unicode(PyDictKeysObject *dk, PyObject *key, Py_hash_t hash, _PyStackRef *value_addr)
{ {
PyDictKeysObject *dk = _Py_atomic_load_ptr(&mp->ma_keys); assert(dk->dk_kind == DICT_KEYS_UNICODE);
if (dk->dk_kind == DICT_KEYS_UNICODE && PyUnicode_CheckExact(key)) { assert(PyUnicode_CheckExact(key));
Py_ssize_t ix = unicodekeys_lookup_unicode_threadsafe(dk, key, hash); Py_ssize_t ix = unicodekeys_lookup_unicode_threadsafe(dk, key, hash);
if (ix == DKIX_EMPTY) { if (ix == DKIX_EMPTY) {
*value_addr = PyStackRef_NULL; *value_addr = PyStackRef_NULL;
@ -1606,6 +1607,20 @@ _Py_dict_lookup_threadsafe_stackref(PyDictObject *mp, PyObject *key, Py_hash_t h
*value_addr = PyStackRef_FromPyObjectSteal(value); *value_addr = PyStackRef_FromPyObjectSteal(value);
return ix; return ix;
} }
return DKIX_KEY_CHANGED;
}
assert(ix == DKIX_KEY_CHANGED);
return ix;
}
Py_ssize_t
_Py_dict_lookup_threadsafe_stackref(PyDictObject *mp, PyObject *key, Py_hash_t hash, _PyStackRef *value_addr)
{
PyDictKeysObject *dk = _Py_atomic_load_ptr(&mp->ma_keys);
if (dk->dk_kind == DICT_KEYS_UNICODE && PyUnicode_CheckExact(key)) {
Py_ssize_t ix = lookup_threadsafe_unicode(dk, key, hash, value_addr);
if (ix != DKIX_KEY_CHANGED) {
return ix;
} }
} }
@ -1646,6 +1661,46 @@ _Py_dict_lookup_threadsafe_stackref(PyDictObject *mp, PyObject *key, Py_hash_t h
#endif #endif
// Looks up the unicode key `key` in the dictionary. Note that `*method` may
// already contain a valid value! See _PyObject_GetMethodStackRef().
int
_PyDict_GetMethodStackRef(PyDictObject *mp, PyObject *key, _PyStackRef *method)
{
assert(PyUnicode_CheckExact(key));
Py_hash_t hash = hash_unicode_key(key);
#ifdef Py_GIL_DISABLED
PyDictKeysObject *dk = _Py_atomic_load_ptr_acquire(&mp->ma_keys);
if (dk->dk_kind == DICT_KEYS_UNICODE) {
_PyStackRef ref;
Py_ssize_t ix = lookup_threadsafe_unicode(dk, key, hash, &ref);
if (ix >= 0) {
assert(!PyStackRef_IsNull(ref));
PyStackRef_XSETREF(*method, ref);
return 1;
}
else if (ix == DKIX_EMPTY) {
return 0;
}
assert(ix == DKIX_KEY_CHANGED);
}
#endif
PyObject *obj;
Py_INCREF(mp);
Py_ssize_t ix = _Py_dict_lookup_threadsafe(mp, key, hash, &obj);
Py_DECREF(mp);
if (ix == DKIX_ERROR) {
PyStackRef_CLEAR(*method);
return -1;
}
else if (ix >= 0 && obj != NULL) {
PyStackRef_XSETREF(*method, PyStackRef_FromPyObjectSteal(obj));
return 1;
}
return 0; // not found
}
int int
_PyDict_HasOnlyStringKeys(PyObject *dict) _PyDict_HasOnlyStringKeys(PyObject *dict)
{ {

View file

@ -1753,20 +1753,15 @@ _PyObject_GetMethodStackRef(PyThreadState *ts, PyObject *obj,
} }
} }
if (dict != NULL) { if (dict != NULL) {
// TODO: use _Py_dict_lookup_threadsafe_stackref assert(PyUnicode_CheckExact(name));
Py_INCREF(dict); int found = _PyDict_GetMethodStackRef((PyDictObject *)dict, name, method);
PyObject *value; if (found < 0) {
if (PyDict_GetItemRef(dict, name, &value) != 0) { assert(PyStackRef_IsNull(*method));
// found or error return -1;
Py_DECREF(dict);
PyStackRef_CLEAR(*method);
if (value != NULL) {
*method = PyStackRef_FromPyObjectSteal(value);
} }
else if (found) {
return 0; return 0;
} }
// not found
Py_DECREF(dict);
} }
if (meth_found) { if (meth_found) {