@@ -1584,34 +1584,49 @@ _Py_dict_lookup_threadsafe(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyOb
15841584 return ix ;
15851585}
15861586
1587+ static Py_ssize_t
1588+ lookup_threadsafe_unicode (PyDictKeysObject * dk , PyObject * key , Py_hash_t hash , _PyStackRef * value_addr )
1589+ {
1590+ assert (dk -> dk_kind == DICT_KEYS_UNICODE );
1591+ assert (PyUnicode_CheckExact (key ));
1592+
1593+ Py_ssize_t ix = unicodekeys_lookup_unicode_threadsafe (dk , key , hash );
1594+ if (ix == DKIX_EMPTY ) {
1595+ * value_addr = PyStackRef_NULL ;
1596+ return ix ;
1597+ }
1598+ else if (ix >= 0 ) {
1599+ PyObject * * addr_of_value = & DK_UNICODE_ENTRIES (dk )[ix ].me_value ;
1600+ PyObject * value = _Py_atomic_load_ptr (addr_of_value );
1601+ if (value == NULL ) {
1602+ * value_addr = PyStackRef_NULL ;
1603+ return DKIX_EMPTY ;
1604+ }
1605+ if (_PyObject_HasDeferredRefcount (value )) {
1606+ * value_addr = (_PyStackRef ){ .bits = (uintptr_t )value | Py_TAG_DEFERRED };
1607+ return ix ;
1608+ }
1609+ if (_Py_TryIncrefCompare (addr_of_value , value )) {
1610+ * value_addr = PyStackRef_FromPyObjectSteal (value );
1611+ return ix ;
1612+ }
1613+ return DKIX_KEY_CHANGED ;
1614+ }
1615+ assert (ix == DKIX_KEY_CHANGED );
1616+ return ix ;
1617+ }
1618+
15871619Py_ssize_t
15881620_Py_dict_lookup_threadsafe_stackref (PyDictObject * mp , PyObject * key , Py_hash_t hash , _PyStackRef * value_addr )
15891621{
15901622 ensure_shared_on_read (mp );
15911623
15921624 PyDictKeysObject * dk = _Py_atomic_load_ptr_acquire (& mp -> ma_keys );
15931625 if (dk -> dk_kind == DICT_KEYS_UNICODE && PyUnicode_CheckExact (key )) {
1594- Py_ssize_t ix = unicodekeys_lookup_unicode_threadsafe (dk , key , hash );
1595- if (ix == DKIX_EMPTY ) {
1596- * value_addr = PyStackRef_NULL ;
1626+ Py_ssize_t ix = lookup_threadsafe_unicode (dk , key , hash , value_addr );
1627+ if (ix != DKIX_KEY_CHANGED ) {
15971628 return ix ;
15981629 }
1599- else if (ix >= 0 ) {
1600- PyObject * * addr_of_value = & DK_UNICODE_ENTRIES (dk )[ix ].me_value ;
1601- PyObject * value = _Py_atomic_load_ptr (addr_of_value );
1602- if (value == NULL ) {
1603- * value_addr = PyStackRef_NULL ;
1604- return DKIX_EMPTY ;
1605- }
1606- if (_PyObject_HasDeferredRefcount (value )) {
1607- * value_addr = (_PyStackRef ){ .bits = (uintptr_t )value | Py_TAG_DEFERRED };
1608- return ix ;
1609- }
1610- if (_Py_TryIncrefCompare (addr_of_value , value )) {
1611- * value_addr = PyStackRef_FromPyObjectSteal (value );
1612- return ix ;
1613- }
1614- }
16151630 }
16161631
16171632 PyObject * obj ;
@@ -1651,6 +1666,54 @@ _Py_dict_lookup_threadsafe_stackref(PyDictObject *mp, PyObject *key, Py_hash_t h
16511666
16521667#endif
16531668
1669+ // Looks up the unicode key `key` in the dictionary. Note that `*method` may
1670+ // already contain a valid value! See _PyObject_GetMethodStackRef().
1671+ int
1672+ _PyDict_GetMethodStackRef (PyDictObject * mp , PyObject * key , _PyStackRef * method )
1673+ {
1674+ assert (PyUnicode_CheckExact (key ));
1675+ Py_hash_t hash = hash_unicode_key (key );
1676+
1677+ #ifdef Py_GIL_DISABLED
1678+ // NOTE: We can only do the fast-path lookup if we are on the owning
1679+ // thread or if the dict is already marked as shared so that the load
1680+ // of ma_keys is safe without a lock. We cannot call ensure_shared_on_read()
1681+ // in this code path without incref'ing the dict because the dict is a
1682+ // borrowed reference protected by QSBR, and acquiring the lock could lead
1683+ // to a quiescent state (allowing the dict to be freed).
1684+ if (_Py_IsOwnedByCurrentThread ((PyObject * )mp ) || IS_DICT_SHARED (mp )) {
1685+ PyDictKeysObject * dk = _Py_atomic_load_ptr_acquire (& mp -> ma_keys );
1686+ if (dk -> dk_kind == DICT_KEYS_UNICODE ) {
1687+ _PyStackRef ref ;
1688+ Py_ssize_t ix = lookup_threadsafe_unicode (dk , key , hash , & ref );
1689+ if (ix >= 0 ) {
1690+ assert (!PyStackRef_IsNull (ref ));
1691+ PyStackRef_XSETREF (* method , ref );
1692+ return 1 ;
1693+ }
1694+ else if (ix == DKIX_EMPTY ) {
1695+ return 0 ;
1696+ }
1697+ assert (ix == DKIX_KEY_CHANGED );
1698+ }
1699+ }
1700+ #endif
1701+
1702+ PyObject * obj ;
1703+ Py_INCREF (mp );
1704+ Py_ssize_t ix = _Py_dict_lookup_threadsafe (mp , key , hash , & obj );
1705+ Py_DECREF (mp );
1706+ if (ix == DKIX_ERROR ) {
1707+ PyStackRef_CLEAR (* method );
1708+ return -1 ;
1709+ }
1710+ else if (ix >= 0 && obj != NULL ) {
1711+ PyStackRef_XSETREF (* method , PyStackRef_FromPyObjectSteal (obj ));
1712+ return 1 ;
1713+ }
1714+ return 0 ; // not found
1715+ }
1716+
16541717int
16551718_PyDict_HasOnlyStringKeys (PyObject * dict )
16561719{
0 commit comments