Skip to content

Commit cbe6ebe

Browse files
gh-134043: use stackrefs for dict lookup in _PyObject_GetMethodStackRef (#136412)
Co-authored-by: Sam Gross <[email protected]>
1 parent 1e9b8f2 commit cbe6ebe

File tree

4 files changed

+90
-31
lines changed

4 files changed

+90
-31
lines changed

Include/internal/pycore_dict.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,8 @@ extern Py_ssize_t _Py_dict_lookup(PyDictObject *mp, PyObject *key, Py_hash_t has
113113
extern Py_ssize_t _Py_dict_lookup_threadsafe(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr);
114114
extern Py_ssize_t _Py_dict_lookup_threadsafe_stackref(PyDictObject *mp, PyObject *key, Py_hash_t hash, _PyStackRef *value_addr);
115115

116+
extern int _PyDict_GetMethodStackRef(PyDictObject *dict, PyObject *name, _PyStackRef *method);
117+
116118
extern Py_ssize_t _PyDict_LookupIndex(PyDictObject *, PyObject *);
117119
extern Py_ssize_t _PyDictKeys_StringLookup(PyDictKeysObject* dictkeys, PyObject *key);
118120

Include/internal/pycore_stackref.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -839,6 +839,13 @@ _Py_TryXGetStackRef(PyObject **src, _PyStackRef *out)
839839

840840
#endif
841841

842+
#define PyStackRef_XSETREF(dst, src) \
843+
do { \
844+
_PyStackRef _tmp_dst_ref = (dst); \
845+
(dst) = (src); \
846+
PyStackRef_XCLOSE(_tmp_dst_ref); \
847+
} while(0)
848+
842849
// Like Py_VISIT but for _PyStackRef fields
843850
#define _Py_VISIT_STACKREF(ref) \
844851
do { \

Objects/dictobject.c

Lines changed: 74 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1581,32 +1581,47 @@ _Py_dict_lookup_threadsafe(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyOb
15811581
return ix;
15821582
}
15831583

1584+
static Py_ssize_t
1585+
lookup_threadsafe_unicode(PyDictKeysObject *dk, PyObject *key, Py_hash_t hash, _PyStackRef *value_addr)
1586+
{
1587+
assert(dk->dk_kind == DICT_KEYS_UNICODE);
1588+
assert(PyUnicode_CheckExact(key));
1589+
1590+
Py_ssize_t ix = unicodekeys_lookup_unicode_threadsafe(dk, key, hash);
1591+
if (ix == DKIX_EMPTY) {
1592+
*value_addr = PyStackRef_NULL;
1593+
return ix;
1594+
}
1595+
else if (ix >= 0) {
1596+
PyObject **addr_of_value = &DK_UNICODE_ENTRIES(dk)[ix].me_value;
1597+
PyObject *value = _Py_atomic_load_ptr(addr_of_value);
1598+
if (value == NULL) {
1599+
*value_addr = PyStackRef_NULL;
1600+
return DKIX_EMPTY;
1601+
}
1602+
if (_PyObject_HasDeferredRefcount(value)) {
1603+
*value_addr = (_PyStackRef){ .bits = (uintptr_t)value | Py_TAG_DEFERRED };
1604+
return ix;
1605+
}
1606+
if (_Py_TryIncrefCompare(addr_of_value, value)) {
1607+
*value_addr = PyStackRef_FromPyObjectSteal(value);
1608+
return ix;
1609+
}
1610+
return DKIX_KEY_CHANGED;
1611+
}
1612+
assert(ix == DKIX_KEY_CHANGED);
1613+
return ix;
1614+
}
1615+
15841616
Py_ssize_t
15851617
_Py_dict_lookup_threadsafe_stackref(PyDictObject *mp, PyObject *key, Py_hash_t hash, _PyStackRef *value_addr)
15861618
{
15871619
PyDictKeysObject *dk = _Py_atomic_load_ptr(&mp->ma_keys);
15881620
if (dk->dk_kind == DICT_KEYS_UNICODE && PyUnicode_CheckExact(key)) {
1589-
Py_ssize_t ix = unicodekeys_lookup_unicode_threadsafe(dk, key, hash);
1590-
if (ix == DKIX_EMPTY) {
1591-
*value_addr = PyStackRef_NULL;
1621+
Py_ssize_t ix = lookup_threadsafe_unicode(dk, key, hash, value_addr);
1622+
if (ix != DKIX_KEY_CHANGED) {
15921623
return ix;
15931624
}
1594-
else if (ix >= 0) {
1595-
PyObject **addr_of_value = &DK_UNICODE_ENTRIES(dk)[ix].me_value;
1596-
PyObject *value = _Py_atomic_load_ptr(addr_of_value);
1597-
if (value == NULL) {
1598-
*value_addr = PyStackRef_NULL;
1599-
return DKIX_EMPTY;
1600-
}
1601-
if (_PyObject_HasDeferredRefcount(value)) {
1602-
*value_addr = (_PyStackRef){ .bits = (uintptr_t)value | Py_TAG_DEFERRED };
1603-
return ix;
1604-
}
1605-
if (_Py_TryIncrefCompare(addr_of_value, value)) {
1606-
*value_addr = PyStackRef_FromPyObjectSteal(value);
1607-
return ix;
1608-
}
1609-
}
16101625
}
16111626

16121627
PyObject *obj;
@@ -1646,6 +1661,46 @@ _Py_dict_lookup_threadsafe_stackref(PyDictObject *mp, PyObject *key, Py_hash_t h
16461661

16471662
#endif
16481663

1664+
// Looks up the unicode key `key` in the dictionary. Note that `*method` may
1665+
// already contain a valid value! See _PyObject_GetMethodStackRef().
1666+
int
1667+
_PyDict_GetMethodStackRef(PyDictObject *mp, PyObject *key, _PyStackRef *method)
1668+
{
1669+
assert(PyUnicode_CheckExact(key));
1670+
Py_hash_t hash = hash_unicode_key(key);
1671+
1672+
#ifdef Py_GIL_DISABLED
1673+
PyDictKeysObject *dk = _Py_atomic_load_ptr_acquire(&mp->ma_keys);
1674+
if (dk->dk_kind == DICT_KEYS_UNICODE) {
1675+
_PyStackRef ref;
1676+
Py_ssize_t ix = lookup_threadsafe_unicode(dk, key, hash, &ref);
1677+
if (ix >= 0) {
1678+
assert(!PyStackRef_IsNull(ref));
1679+
PyStackRef_XSETREF(*method, ref);
1680+
return 1;
1681+
}
1682+
else if (ix == DKIX_EMPTY) {
1683+
return 0;
1684+
}
1685+
assert(ix == DKIX_KEY_CHANGED);
1686+
}
1687+
#endif
1688+
1689+
PyObject *obj;
1690+
Py_INCREF(mp);
1691+
Py_ssize_t ix = _Py_dict_lookup_threadsafe(mp, key, hash, &obj);
1692+
Py_DECREF(mp);
1693+
if (ix == DKIX_ERROR) {
1694+
PyStackRef_CLEAR(*method);
1695+
return -1;
1696+
}
1697+
else if (ix >= 0 && obj != NULL) {
1698+
PyStackRef_XSETREF(*method, PyStackRef_FromPyObjectSteal(obj));
1699+
return 1;
1700+
}
1701+
return 0; // not found
1702+
}
1703+
16491704
int
16501705
_PyDict_HasOnlyStringKeys(PyObject *dict)
16511706
{

Objects/object.c

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1753,20 +1753,15 @@ _PyObject_GetMethodStackRef(PyThreadState *ts, PyObject *obj,
17531753
}
17541754
}
17551755
if (dict != NULL) {
1756-
// TODO: use _Py_dict_lookup_threadsafe_stackref
1757-
Py_INCREF(dict);
1758-
PyObject *value;
1759-
if (PyDict_GetItemRef(dict, name, &value) != 0) {
1760-
// found or error
1761-
Py_DECREF(dict);
1762-
PyStackRef_CLEAR(*method);
1763-
if (value != NULL) {
1764-
*method = PyStackRef_FromPyObjectSteal(value);
1765-
}
1756+
assert(PyUnicode_CheckExact(name));
1757+
int found = _PyDict_GetMethodStackRef((PyDictObject *)dict, name, method);
1758+
if (found < 0) {
1759+
assert(PyStackRef_IsNull(*method));
1760+
return -1;
1761+
}
1762+
else if (found) {
17661763
return 0;
17671764
}
1768-
// not found
1769-
Py_DECREF(dict);
17701765
}
17711766

17721767
if (meth_found) {

0 commit comments

Comments
 (0)