Skip to content

Commit 71864c1

Browse files
Merge pull request #19 from colesbury/dict-refactor
2 parents 5e79d52 + bbc7fb8 commit 71864c1

File tree

4 files changed

+74
-88
lines changed

4 files changed

+74
-88
lines changed

Include/internal/pycore_dict.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,8 @@ extern void _PyDictKeys_DecRef(PyDictKeysObject *keys);
112112
extern Py_ssize_t _Py_dict_lookup(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr);
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);
115-
extern Py_ssize_t _Py_dict_lookup_unicode_threadsafe_stackref(PyDictObject *mp, PyObject *key, Py_hash_t hash, _PyStackRef *value_addr);
115+
116+
extern int _PyDict_GetMethodStackRef(PyDictObject *dict, PyObject *name, _PyStackRef *method);
116117

117118
extern Py_ssize_t _PyDict_LookupIndex(PyDictObject *, PyObject *);
118119
extern Py_ssize_t _PyDictKeys_StringLookup(PyDictKeysObject* dictkeys, PyObject *key);

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: 61 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1581,84 +1581,51 @@ _Py_dict_lookup_threadsafe(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyOb
15811581
return ix;
15821582
}
15831583

1584-
1585-
Py_ssize_t
1586-
_Py_dict_lookup_threadsafe_stackref(PyDictObject *mp, PyObject *key, Py_hash_t hash, _PyStackRef *value_addr)
1584+
static Py_ssize_t
1585+
lookup_threadsafe_unicode(PyDictKeysObject *dk, PyObject *key, Py_hash_t hash, _PyStackRef *value_addr)
15871586
{
1588-
PyDictKeysObject *dk = _Py_atomic_load_ptr(&mp->ma_keys);
1589-
if (dk->dk_kind == DICT_KEYS_UNICODE && PyUnicode_CheckExact(key)) {
1590-
Py_ssize_t ix = unicodekeys_lookup_unicode_threadsafe(dk, key, hash);
1591-
if (ix == DKIX_EMPTY) {
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) {
15921599
*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 };
15931604
return ix;
15941605
}
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-
}
1606+
if (_Py_TryIncrefCompare(addr_of_value, value)) {
1607+
*value_addr = PyStackRef_FromPyObjectSteal(value);
1608+
return ix;
16101609
}
1610+
return DKIX_KEY_CHANGED;
16111611
}
1612-
1613-
PyObject *obj;
1614-
Py_ssize_t ix = _Py_dict_lookup_threadsafe(mp, key, hash, &obj);
1615-
if (ix >= 0 && obj != NULL) {
1616-
*value_addr = PyStackRef_FromPyObjectSteal(obj);
1617-
}
1618-
else {
1619-
*value_addr = PyStackRef_NULL;
1620-
}
1612+
assert(ix == DKIX_KEY_CHANGED);
16211613
return ix;
16221614
}
16231615

1624-
// This is similar to _Py_dict_lookup_threadsafe_stackref() but
1625-
// it is used when dict is borrowed reference and key is known to be unicode.
1626-
// It avoids increfing the dict if dict only has unicode keys in which case
1627-
// the lookup is safe, otherwise it increfs the dict and lookups the key.
1628-
16291616
Py_ssize_t
1630-
_Py_dict_lookup_unicode_threadsafe_stackref(PyDictObject *mp, PyObject *key, Py_hash_t hash, _PyStackRef *value_addr)
1617+
_Py_dict_lookup_threadsafe_stackref(PyDictObject *mp, PyObject *key, Py_hash_t hash, _PyStackRef *value_addr)
16311618
{
1632-
assert(PyUnicode_CheckExact(key));
16331619
PyDictKeysObject *dk = _Py_atomic_load_ptr(&mp->ma_keys);
1634-
if (dk->dk_kind == DICT_KEYS_UNICODE) {
1635-
Py_ssize_t ix = unicodekeys_lookup_unicode_threadsafe(dk, key, hash);
1636-
if (ix == DKIX_EMPTY) {
1637-
*value_addr = PyStackRef_NULL;
1620+
if (dk->dk_kind == DICT_KEYS_UNICODE && PyUnicode_CheckExact(key)) {
1621+
Py_ssize_t ix = lookup_threadsafe_unicode(dk, key, hash, value_addr);
1622+
if (ix != DKIX_KEY_CHANGED) {
16381623
return ix;
16391624
}
1640-
else if (ix >= 0) {
1641-
PyObject **addr_of_value = &DK_UNICODE_ENTRIES(dk)[ix].me_value;
1642-
PyObject *value = _Py_atomic_load_ptr(addr_of_value);
1643-
if (value == NULL) {
1644-
*value_addr = PyStackRef_NULL;
1645-
return DKIX_EMPTY;
1646-
}
1647-
if (_PyObject_HasDeferredRefcount(value)) {
1648-
*value_addr = (_PyStackRef){ .bits = (uintptr_t)value | Py_TAG_DEFERRED };
1649-
return ix;
1650-
}
1651-
if (_Py_TryIncrefCompare(addr_of_value, value)) {
1652-
*value_addr = PyStackRef_FromPyObjectSteal(value);
1653-
return ix;
1654-
}
1655-
}
16561625
}
16571626

16581627
PyObject *obj;
1659-
Py_INCREF(mp);
16601628
Py_ssize_t ix = _Py_dict_lookup_threadsafe(mp, key, hash, &obj);
1661-
Py_DECREF(mp);
16621629
if (ix >= 0 && obj != NULL) {
16631630
*value_addr = PyStackRef_FromPyObjectSteal(obj);
16641631
}
@@ -1692,25 +1659,48 @@ _Py_dict_lookup_threadsafe_stackref(PyDictObject *mp, PyObject *key, Py_hash_t h
16921659
return ix;
16931660
}
16941661

1695-
Py_ssize_t
1696-
_Py_dict_lookup_unicode_threadsafe_stackref(PyDictObject *mp, PyObject *key, Py_hash_t hash, _PyStackRef *value_addr)
1662+
#endif
1663+
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)
16971668
{
16981669
assert(PyUnicode_CheckExact(key));
1699-
PyObject *val;
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;
17001690
Py_INCREF(mp);
1701-
Py_ssize_t ix = _Py_dict_lookup(mp, key, hash, &val);
1691+
Py_ssize_t ix = _Py_dict_lookup_threadsafe(mp, key, hash, &obj);
17021692
Py_DECREF(mp);
1703-
if (ix >= 0 && val != NULL) {
1704-
*value_addr = PyStackRef_FromPyObjectNew(val);
1693+
if (ix == DKIX_ERROR) {
1694+
PyStackRef_CLEAR(*method);
1695+
return -1;
17051696
}
1706-
else {
1707-
*value_addr = PyStackRef_NULL;
1697+
else if (ix >= 0 && obj != NULL) {
1698+
PyStackRef_XSETREF(*method, PyStackRef_FromPyObjectSteal(obj));
1699+
return 1;
17081700
}
1709-
return ix;
1701+
return 0; // not found
17101702
}
17111703

1712-
#endif
1713-
17141704
int
17151705
_PyDict_HasOnlyStringKeys(PyObject *dict)
17161706
{

Objects/object.c

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1754,24 +1754,12 @@ _PyObject_GetMethodStackRef(PyThreadState *ts, PyObject *obj,
17541754
}
17551755
if (dict != NULL) {
17561756
assert(PyUnicode_CheckExact(name));
1757-
Py_hash_t hash = _PyObject_HashFast(name);
1758-
// cannot fail for exact unicode
1759-
assert(hash != -1);
1760-
// ref is not visible to gc so there should be
1761-
// no escaping calls before assigning it to method
1762-
_PyStackRef ref;
1763-
Py_ssize_t ix = _Py_dict_lookup_unicode_threadsafe_stackref((PyDictObject *)dict,
1764-
name, hash, &ref);
1765-
if (ix == DKIX_ERROR) {
1766-
// error
1767-
PyStackRef_CLEAR(*method);
1757+
int found = _PyDict_GetMethodStackRef(dict, name, method);
1758+
if (found < 0) {
1759+
assert(PyStackRef_IsNull(*method));
17681760
return -1;
17691761
}
1770-
else if (!PyStackRef_IsNull(ref)) {
1771-
// found
1772-
_PyStackRef tmp = *method;
1773-
*method = ref;
1774-
PyStackRef_XCLOSE(tmp);
1762+
else if (found) {
17751763
return 0;
17761764
}
17771765
}

0 commit comments

Comments
 (0)