@@ -161,14 +161,16 @@ ASSERT_DICT_LOCKED(PyObject *op)
161
161
#define INCREF_KEYS (dk ) _Py_atomic_add_ssize(&dk->dk_refcnt, 1)
162
162
// Dec refs the keys object, giving the previous value
163
163
#define DECREF_KEYS (dk ) _Py_atomic_add_ssize(&dk->dk_refcnt, -1)
164
+ #define LOAD_KEYS_NENTIRES (keys ) _Py_atomic_load_ssize_relaxed(&keys->dk_nentries)
165
+
164
166
static inline void split_keys_entry_added (PyDictKeysObject * keys )
165
167
{
166
168
ASSERT_KEYS_LOCKED (keys );
167
169
168
170
// We increase before we decrease so we never get too small of a value
169
171
// when we're racing with reads
170
- _Py_atomic_store_ssize (& keys -> dk_nentries , keys -> dk_nentries + 1 );
171
- _Py_atomic_store_ssize (& keys -> dk_usable , keys -> dk_usable - 1 );
172
+ _Py_atomic_store_ssize_relaxed (& keys -> dk_nentries , keys -> dk_nentries + 1 );
173
+ _Py_atomic_store_ssize_release (& keys -> dk_usable , keys -> dk_usable - 1 );
172
174
}
173
175
174
176
#else /* Py_GIL_DISABLED */
@@ -181,6 +183,8 @@ static inline void split_keys_entry_added(PyDictKeysObject *keys)
181
183
#define STORE_SHARED_KEY (key , value ) key = value
182
184
#define INCREF_KEYS (dk ) dk->dk_refcnt++
183
185
#define DECREF_KEYS (dk ) dk->dk_refcnt--
186
+ #define LOAD_KEYS_NENTIRES (keys ) keys->dk_nentries
187
+
184
188
static inline void split_keys_entry_added (PyDictKeysObject * keys )
185
189
{
186
190
keys -> dk_usable -- ;
@@ -534,7 +538,7 @@ static PyDictKeysObject empty_keys_struct = {
534
538
0 , /* dk_log2_index_bytes */
535
539
DICT_KEYS_UNICODE , /* dk_kind */
536
540
#ifdef Py_GIL_DISABLED
537
- {0 }, /* dk_lock */
541
+ {0 }, /* dk_mutex */
538
542
#endif
539
543
1 , /* dk_version */
540
544
0 , /* dk_usable (immutable) */
@@ -814,9 +818,9 @@ shared_keys_usable_size(PyDictKeysObject *keys)
814
818
{
815
819
#ifdef Py_GIL_DISABLED
816
820
// dk_usable will decrease for each instance that is created and each
817
- // value that is added. dk_entries will increase for each value that
821
+ // value that is added. dk_nentries will increase for each value that
818
822
// is added. We want to always return the right value or larger.
819
- // We therefore increase dk_entries first and we decrease dk_usable
823
+ // We therefore increase dk_nentries first and we decrease dk_usable
820
824
// second, and conversely here we read dk_usable first and dk_entries
821
825
// second (to avoid the case where we read entries before the increment
822
826
// and read usable after the decrement)
@@ -835,15 +839,13 @@ new_dict_with_shared_keys(PyInterpreterState *interp, PyDictKeysObject *keys)
835
839
PyDictValues * values = new_values (size );
836
840
if (values == NULL ) {
837
841
dictkeys_decref (interp , keys );
838
- UNLOCK_KEYS (keys );
839
842
return PyErr_NoMemory ();
840
843
}
841
844
((char * )values )[-2 ] = 0 ;
842
845
for (size_t i = 0 ; i < size ; i ++ ) {
843
846
values -> values [i ] = NULL ;
844
847
}
845
- PyObject * res = new_dict (interp , keys , values , 0 , 1 );
846
- return res ;
848
+ return new_dict (interp , keys , values , 0 , 1 );
847
849
}
848
850
849
851
@@ -1255,7 +1257,7 @@ insertion_resize(PyInterpreterState *interp, PyDictObject *mp, int unicode)
1255
1257
}
1256
1258
1257
1259
static Py_ssize_t
1258
- insert_into_dictkeys (PyDictKeysObject * keys , PyObject * name )
1260
+ insert_into_splitdictkeys (PyDictKeysObject * keys , PyObject * name )
1259
1261
{
1260
1262
assert (PyUnicode_CheckExact (name ));
1261
1263
ASSERT_KEYS_LOCKED (keys );
@@ -1290,7 +1292,7 @@ insert_into_dictkeys(PyDictKeysObject *keys, PyObject *name)
1290
1292
1291
1293
static inline int
1292
1294
insert_combined_dict (PyInterpreterState * interp , PyDictObject * mp ,
1293
- Py_hash_t hash , PyObject * key , PyObject * value , int unicode )
1295
+ Py_hash_t hash , PyObject * key , PyObject * value )
1294
1296
{
1295
1297
if (mp -> ma_keys -> dk_usable <= 0 ) {
1296
1298
/* Need to resize. */
@@ -1302,7 +1304,7 @@ insert_combined_dict(PyInterpreterState *interp, PyDictObject *mp,
1302
1304
Py_ssize_t hashpos = find_empty_slot (mp -> ma_keys , hash );
1303
1305
dictkeys_set_index (mp -> ma_keys , hashpos , mp -> ma_keys -> dk_nentries );
1304
1306
1305
- if (unicode ) {
1307
+ if (DK_IS_UNICODE ( mp -> ma_keys ) ) {
1306
1308
PyDictUnicodeEntry * ep ;
1307
1309
ep = & DK_UNICODE_ENTRIES (mp -> ma_keys )[mp -> ma_keys -> dk_nentries ];
1308
1310
ep -> me_key = key ;
@@ -1329,14 +1331,16 @@ insert_split_dict(PyInterpreterState *interp, PyDictObject *mp,
1329
1331
LOCK_KEYS (keys );
1330
1332
if (keys -> dk_usable <= 0 ) {
1331
1333
/* Need to resize. */
1332
- if (insertion_resize (interp , mp , 1 ) < 0 ) {
1333
- UNLOCK_KEYS (keys );
1334
+ dictkeys_incref (keys );
1335
+ int ins = insertion_resize (interp , mp , 1 );
1336
+ dictkeys_decref (interp , keys );
1337
+ UNLOCK_KEYS (keys );
1338
+ if (ins < 0 ) {
1334
1339
return -1 ;
1335
1340
}
1336
1341
assert (!_PyDict_HasSplitTable (mp ));
1337
1342
assert (DK_IS_UNICODE (keys ));
1338
- UNLOCK_KEYS (keys );
1339
- return insert_combined_dict (interp , mp , hash , key , value , 1 );
1343
+ return insert_combined_dict (interp , mp , hash , key , value );
1340
1344
}
1341
1345
1342
1346
Py_ssize_t hashpos = find_empty_slot (keys , hash );
@@ -1414,9 +1418,8 @@ insertdict(PyInterpreterState *interp, PyDictObject *mp,
1414
1418
mp -> ma_keys -> dk_version = 0 ;
1415
1419
assert (old_value == NULL );
1416
1420
1417
- int unicode = DK_IS_UNICODE (mp -> ma_keys );
1418
- if (!unicode || !_PyDict_HasSplitTable (mp )) {
1419
- if (insert_combined_dict (interp , mp , hash , key , value , unicode ) < 0 ) {
1421
+ if (!_PyDict_HasSplitTable (mp )) {
1422
+ if (insert_combined_dict (interp , mp , hash , key , value ) < 0 ) {
1420
1423
goto Fail ;
1421
1424
}
1422
1425
} else {
@@ -3597,21 +3600,7 @@ dict_equal_lock_held(PyDictObject *a, PyDictObject *b)
3597
3600
/* can't be equal if # of entries differ */
3598
3601
return 0 ;
3599
3602
/* Same # of entries -- check all of 'em. Exit early on any diff. */
3600
- Py_ssize_t n_entries ;
3601
- #ifdef Py_GIL_DISABLED
3602
- if (_PyDict_HasSplitTable (a )) {
3603
- // New entries can be appended, but existing ones won't change, and
3604
- // our keys won't change because the dict is locked. So capture
3605
- // the current number of keys at entry.
3606
- LOCK_KEYS (a -> ma_keys );
3607
- n_entries = a -> ma_keys -> dk_nentries ;
3608
- UNLOCK_KEYS (a -> ma_keys );
3609
- } else
3610
- #endif
3611
- {
3612
- n_entries = a -> ma_keys -> dk_nentries ;
3613
- }
3614
- for (i = 0 ; i < n_entries ; i ++ ) {
3603
+ for (i = 0 ; i < LOAD_KEYS_NENTIRES (a -> ma_keys ); i ++ ) {
3615
3604
PyObject * key , * aval ;
3616
3605
Py_hash_t hash ;
3617
3606
if (DK_IS_UNICODE (a -> ma_keys )) {
@@ -3825,9 +3814,8 @@ dict_setdefault_ref_lock_held(PyObject *d, PyObject *key, PyObject *default_valu
3825
3814
mp -> ma_keys -> dk_version = 0 ;
3826
3815
value = default_value ;
3827
3816
3828
- int unicode = DK_IS_UNICODE (mp -> ma_keys );
3829
- if (!unicode || !_PyDict_HasSplitTable (mp )) {
3830
- if (insert_combined_dict (interp , mp , hash , Py_NewRef (key ), Py_NewRef (value ), unicode ) < 0 ) {
3817
+ if (!_PyDict_HasSplitTable (mp )) {
3818
+ if (insert_combined_dict (interp , mp , hash , Py_NewRef (key ), Py_NewRef (value )) < 0 ) {
3831
3819
Py_DECREF (key );
3832
3820
Py_DECREF (value );
3833
3821
if (result ) {
@@ -6037,11 +6025,13 @@ _PyObject_InitInlineValues(PyObject *obj, PyTypeObject *tp)
6037
6025
PyDictKeysObject * keys = CACHED_KEYS (tp );
6038
6026
assert (keys != NULL );
6039
6027
#ifdef Py_GIL_DISABLED
6040
- Py_ssize_t usable = keys -> dk_usable ;
6041
- while (usable > 1 ) {
6042
- if (_Py_atomic_compare_exchange_ssize (& keys -> dk_usable , & usable , usable - 1 )) {
6043
- break ;
6028
+ Py_ssize_t usable = _Py_atomic_load_ssize_relaxed (& keys -> dk_usable );
6029
+ if (usable > 1 ) {
6030
+ LOCK_KEYS (keys );
6031
+ if (keys -> dk_usable > 1 ) {
6032
+ _Py_atomic_store_ssize (& keys -> dk_usable , keys -> dk_usable - 1 );
6044
6033
}
6034
+ UNLOCK_KEYS (keys );
6045
6035
}
6046
6036
#else
6047
6037
if (keys -> dk_usable > 1 ) {
@@ -6179,7 +6169,7 @@ _PyObject_StoreInstanceAttribute(PyObject *obj, PyDictValues *values,
6179
6169
Py_ssize_t ix = DKIX_EMPTY ;
6180
6170
if (PyUnicode_CheckExact (name )) {
6181
6171
LOCK_KEYS (keys );
6182
- ix = insert_into_dictkeys (keys , name );
6172
+ ix = insert_into_splitdictkeys (keys , name );
6183
6173
#ifdef Py_STATS
6184
6174
if (ix == DKIX_EMPTY ) {
6185
6175
if (PyUnicode_CheckExact (name )) {
0 commit comments