@@ -162,6 +162,16 @@ ASSERT_DICT_LOCKED(PyObject *op)
162
162
assert(_Py_IsOwnedByCurrentThread((PyObject *)mp) || IS_DICT_SHARED(mp));
163
163
#define LOAD_KEYS_NENTRIES (d )
164
164
165
+ #define LOCK_KEYS_IF_SPLIT (keys , kind ) \
166
+ if (kind == DICT_KEYS_SPLIT) { \
167
+ LOCK_KEYS(dk); \
168
+ }
169
+
170
+ #define UNLOCK_KEYS_IF_SPLIT (keys , kind ) \
171
+ if (kind == DICT_KEYS_SPLIT) { \
172
+ UNLOCK_KEYS(dk); \
173
+ }
174
+
165
175
static inline Py_ssize_t
166
176
load_keys_nentries (PyDictObject * mp )
167
177
{
@@ -195,6 +205,9 @@ set_values(PyDictObject *mp, PyDictValues *values)
195
205
#define DECREF_KEYS (dk ) _Py_atomic_add_ssize(&dk->dk_refcnt, -1)
196
206
#define LOAD_KEYS_NENTIRES (keys ) _Py_atomic_load_ssize_relaxed(&keys->dk_nentries)
197
207
208
+ #define INCREF_KEYS_FT (dk ) INCREF_KEYS(dk)
209
+ #define DECREF_KEYS_FT (dk ) INCREF_KEYS(dk)
210
+
198
211
static inline void split_keys_entry_added (PyDictKeysObject * keys )
199
212
{
200
213
ASSERT_KEYS_LOCKED (keys );
@@ -216,6 +229,8 @@ static inline void split_keys_entry_added(PyDictKeysObject *keys)
216
229
#define INCREF_KEYS (dk ) dk->dk_refcnt++
217
230
#define DECREF_KEYS (dk ) dk->dk_refcnt--
218
231
#define LOAD_KEYS_NENTIRES (keys ) keys->dk_nentries
232
+ #define LOCK_KEYS_IF_SPLIT (keys , kind )
233
+ #define UNLOCK_KEYS_IF_SPLIT (keys , kind )
219
234
#define IS_DICT_SHARED (mp ) (false)
220
235
#define SET_DICT_SHARED (mp )
221
236
#define LOAD_INDEX (keys , size , idx ) ((const int##size##_t*)(keys->dk_indices))[idx]
@@ -1192,17 +1207,24 @@ _Py_dict_lookup(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **valu
1192
1207
PyDictKeysObject * dk ;
1193
1208
DictKeysKind kind ;
1194
1209
Py_ssize_t ix ;
1195
- // TODO: Thread safety
1196
1210
start :
1197
1211
dk = mp -> ma_keys ;
1198
1212
kind = dk -> dk_kind ;
1199
1213
1200
1214
if (kind != DICT_KEYS_GENERAL ) {
1201
1215
if (PyUnicode_CheckExact (key )) {
1216
+ LOCK_KEYS_IF_SPLIT (dk , kind );
1202
1217
ix = unicodekeys_lookup_unicode (dk , key , hash );
1218
+ UNLOCK_KEYS_IF_SPLIT (dk , kind );
1203
1219
}
1204
1220
else {
1221
+ INCREF_KEYS_FT (dk );
1222
+ LOCK_KEYS_IF_SPLIT (dk , kind );
1223
+
1205
1224
ix = unicodekeys_lookup_generic (mp , dk , key , hash );
1225
+
1226
+ UNLOCK_KEYS_IF_SPLIT (dk , kind );
1227
+ DECREF_KEYS_FT (dk );
1206
1228
if (ix == DKIX_KEY_CHANGED ) {
1207
1229
goto start ;
1208
1230
}
@@ -1443,6 +1465,7 @@ _Py_dict_lookup_threadsafe(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyOb
1443
1465
Py_DECREF (value );
1444
1466
goto read_failed ;
1445
1467
}
1468
+
1446
1469
}
1447
1470
}
1448
1471
else {
@@ -1709,7 +1732,38 @@ insertdict(PyInterpreterState *interp, PyDictObject *mp,
1709
1732
assert (mp -> ma_keys -> dk_kind == DICT_KEYS_GENERAL );
1710
1733
}
1711
1734
1712
- Py_ssize_t ix = _Py_dict_lookup (mp , key , hash , & old_value );
1735
+ Py_ssize_t ix ;
1736
+ #ifdef Py_GIL_DISABLED
1737
+ PyDictKeysObject * dk = mp -> ma_keys ;
1738
+ DictKeysKind kind = dk -> dk_kind ;
1739
+
1740
+ if (kind == DICT_KEYS_SPLIT ) {
1741
+ // Split keys can be mutated by other dictionaries, so we need
1742
+ // to do a threadsafe lookup here. This is basically the split-key
1743
+ // half of _Py_dict_lookup_threadsafe and avoids locking the
1744
+ // shared keys if we can
1745
+ if (PyUnicode_CheckExact (key )) {
1746
+ ix = unicodekeys_lookup_unicode_threadsafe (dk , key , hash );
1747
+ }
1748
+ else {
1749
+ ix = unicodekeys_lookup_generic_threadsafe (mp , dk , key , hash );
1750
+ }
1751
+
1752
+ if (ix >= 0 ) {
1753
+ old_value = mp -> ma_values -> values [ix ];
1754
+ }
1755
+ else if (ix == DKIX_KEY_CHANGED ) {
1756
+ ix = _Py_dict_lookup (mp , key , hash , & old_value );
1757
+ }
1758
+ else {
1759
+ old_value = NULL ;
1760
+ }
1761
+ } else {
1762
+ ix = _Py_dict_lookup (mp , key , hash , & old_value );
1763
+ }
1764
+ #else
1765
+ ix = _Py_dict_lookup (mp , key , hash , & old_value );
1766
+ #endif
1713
1767
if (ix == DKIX_ERROR )
1714
1768
goto Fail ;
1715
1769
0 commit comments