@@ -169,16 +169,15 @@ ASSERT_DICT_LOCKED(PyObject *op)
169
169
#define STORE_INDEX (keys , size , idx , value ) _Py_atomic_store_int##size##_relaxed(&((int##size##_t*)keys->dk_indices)[idx], (int##size##_t)value);
170
170
#define ASSERT_OWNED_OR_SHARED (mp ) \
171
171
assert(_Py_IsOwnedByCurrentThread((PyObject *)mp) || IS_DICT_SHARED(mp));
172
- #define LOAD_KEYS_NENTRIES (d )
173
172
174
173
#define LOCK_KEYS_IF_SPLIT (keys , kind ) \
175
174
if (kind == DICT_KEYS_SPLIT) { \
176
- LOCK_KEYS(dk); \
175
+ LOCK_KEYS(keys); \
177
176
}
178
177
179
178
#define UNLOCK_KEYS_IF_SPLIT (keys , kind ) \
180
179
if (kind == DICT_KEYS_SPLIT) { \
181
- UNLOCK_KEYS(dk); \
180
+ UNLOCK_KEYS(keys); \
182
181
}
183
182
184
183
static inline Py_ssize_t
@@ -212,7 +211,7 @@ set_values(PyDictObject *mp, PyDictValues *values)
212
211
#define INCREF_KEYS (dk ) _Py_atomic_add_ssize(&dk->dk_refcnt, 1)
213
212
// Dec refs the keys object, giving the previous value
214
213
#define DECREF_KEYS (dk ) _Py_atomic_add_ssize(&dk->dk_refcnt, -1)
215
- #define LOAD_KEYS_NENTIRES (keys ) _Py_atomic_load_ssize_relaxed(&keys->dk_nentries)
214
+ #define LOAD_KEYS_NENTRIES (keys ) _Py_atomic_load_ssize_relaxed(&keys->dk_nentries)
216
215
217
216
#define INCREF_KEYS_FT (dk ) dictkeys_incref(dk)
218
217
#define DECREF_KEYS_FT (dk , shared ) dictkeys_decref(_PyInterpreterState_GET(), dk, shared)
@@ -239,7 +238,7 @@ static inline void split_keys_entry_added(PyDictKeysObject *keys)
239
238
#define STORE_SHARED_KEY (key , value ) key = value
240
239
#define INCREF_KEYS (dk ) dk->dk_refcnt++
241
240
#define DECREF_KEYS (dk ) dk->dk_refcnt--
242
- #define LOAD_KEYS_NENTIRES (keys ) keys->dk_nentries
241
+ #define LOAD_KEYS_NENTRIES (keys ) keys->dk_nentries
243
242
#define INCREF_KEYS_FT (dk )
244
243
#define DECREF_KEYS_FT (dk , shared )
245
244
#define LOCK_KEYS_IF_SPLIT (keys , kind )
@@ -694,10 +693,15 @@ _PyDict_CheckConsistency(PyObject *op, int check_content)
694
693
int splitted = _PyDict_HasSplitTable (mp );
695
694
Py_ssize_t usable = USABLE_FRACTION (DK_SIZE (keys ));
696
695
696
+ // In the free-threaded build, shared keys may be concurrently modified,
697
+ // so use atomic loads.
698
+ Py_ssize_t dk_usable = FT_ATOMIC_LOAD_SSIZE_ACQUIRE (keys -> dk_usable );
699
+ Py_ssize_t dk_nentries = FT_ATOMIC_LOAD_SSIZE_ACQUIRE (keys -> dk_nentries );
700
+
697
701
CHECK (0 <= mp -> ma_used && mp -> ma_used <= usable );
698
- CHECK (0 <= keys -> dk_usable && keys -> dk_usable <= usable );
699
- CHECK (0 <= keys -> dk_nentries && keys -> dk_nentries <= usable );
700
- CHECK (keys -> dk_usable + keys -> dk_nentries <= usable );
702
+ CHECK (0 <= dk_usable && dk_usable <= usable );
703
+ CHECK (0 <= dk_nentries && dk_nentries <= usable );
704
+ CHECK (dk_usable + dk_nentries <= usable );
701
705
702
706
if (!splitted ) {
703
707
/* combined table */
@@ -714,6 +718,7 @@ _PyDict_CheckConsistency(PyObject *op, int check_content)
714
718
}
715
719
716
720
if (check_content ) {
721
+ LOCK_KEYS_IF_SPLIT (keys , keys -> dk_kind );
717
722
for (Py_ssize_t i = 0 ; i < DK_SIZE (keys ); i ++ ) {
718
723
Py_ssize_t ix = dictkeys_get_index (keys , i );
719
724
CHECK (DKIX_DUMMY <= ix && ix <= usable );
@@ -769,6 +774,7 @@ _PyDict_CheckConsistency(PyObject *op, int check_content)
769
774
CHECK (mp -> ma_values -> values [index ] != NULL );
770
775
}
771
776
}
777
+ UNLOCK_KEYS_IF_SPLIT (keys , keys -> dk_kind );
772
778
}
773
779
return 1 ;
774
780
@@ -4037,7 +4043,7 @@ dict_equal_lock_held(PyDictObject *a, PyDictObject *b)
4037
4043
/* can't be equal if # of entries differ */
4038
4044
return 0 ;
4039
4045
/* Same # of entries -- check all of 'em. Exit early on any diff. */
4040
- for (i = 0 ; i < LOAD_KEYS_NENTIRES (a -> ma_keys ); i ++ ) {
4046
+ for (i = 0 ; i < LOAD_KEYS_NENTRIES (a -> ma_keys ); i ++ ) {
4041
4047
PyObject * key , * aval ;
4042
4048
Py_hash_t hash ;
4043
4049
if (DK_IS_UNICODE (a -> ma_keys )) {
0 commit comments