@@ -165,16 +165,15 @@ ASSERT_DICT_LOCKED(PyObject *op)
165
165
#define STORE_INDEX (keys , size , idx , value ) _Py_atomic_store_int##size##_relaxed(&((int##size##_t*)keys->dk_indices)[idx], (int##size##_t)value);
166
166
#define ASSERT_OWNED_OR_SHARED (mp ) \
167
167
assert(_Py_IsOwnedByCurrentThread((PyObject *)mp) || IS_DICT_SHARED(mp));
168
- #define LOAD_KEYS_NENTRIES (d )
169
168
170
169
#define LOCK_KEYS_IF_SPLIT (keys , kind ) \
171
170
if (kind == DICT_KEYS_SPLIT) { \
172
- LOCK_KEYS(dk); \
171
+ LOCK_KEYS(keys); \
173
172
}
174
173
175
174
#define UNLOCK_KEYS_IF_SPLIT (keys , kind ) \
176
175
if (kind == DICT_KEYS_SPLIT) { \
177
- UNLOCK_KEYS(dk); \
176
+ UNLOCK_KEYS(keys); \
178
177
}
179
178
180
179
static inline Py_ssize_t
@@ -208,7 +207,7 @@ set_values(PyDictObject *mp, PyDictValues *values)
208
207
#define INCREF_KEYS (dk ) _Py_atomic_add_ssize(&dk->dk_refcnt, 1)
209
208
// Dec refs the keys object, giving the previous value
210
209
#define DECREF_KEYS (dk ) _Py_atomic_add_ssize(&dk->dk_refcnt, -1)
211
- #define LOAD_KEYS_NENTIRES (keys ) _Py_atomic_load_ssize_relaxed(&keys->dk_nentries)
210
+ #define LOAD_KEYS_NENTRIES (keys ) _Py_atomic_load_ssize_relaxed(&keys->dk_nentries)
212
211
213
212
#define INCREF_KEYS_FT (dk ) dictkeys_incref(dk)
214
213
#define DECREF_KEYS_FT (dk , shared ) dictkeys_decref(_PyInterpreterState_GET(), dk, shared)
@@ -234,7 +233,7 @@ static inline void split_keys_entry_added(PyDictKeysObject *keys)
234
233
#define STORE_SHARED_KEY (key , value ) key = value
235
234
#define INCREF_KEYS (dk ) dk->dk_refcnt++
236
235
#define DECREF_KEYS (dk ) dk->dk_refcnt--
237
- #define LOAD_KEYS_NENTIRES (keys ) keys->dk_nentries
236
+ #define LOAD_KEYS_NENTRIES (keys ) keys->dk_nentries
238
237
#define INCREF_KEYS_FT (dk )
239
238
#define DECREF_KEYS_FT (dk , shared )
240
239
#define LOCK_KEYS_IF_SPLIT (keys , kind )
@@ -689,10 +688,15 @@ _PyDict_CheckConsistency(PyObject *op, int check_content)
689
688
int splitted = _PyDict_HasSplitTable (mp );
690
689
Py_ssize_t usable = USABLE_FRACTION (DK_SIZE (keys ));
691
690
691
+ // In the free-threaded build, shared keys may be concurrently modified,
692
+ // so use atomic loads.
693
+ Py_ssize_t dk_usable = FT_ATOMIC_LOAD_SSIZE_ACQUIRE (keys -> dk_usable );
694
+ Py_ssize_t dk_nentries = FT_ATOMIC_LOAD_SSIZE_ACQUIRE (keys -> dk_nentries );
695
+
692
696
CHECK (0 <= mp -> ma_used && mp -> ma_used <= usable );
693
- CHECK (0 <= keys -> dk_usable && keys -> dk_usable <= usable );
694
- CHECK (0 <= keys -> dk_nentries && keys -> dk_nentries <= usable );
695
- CHECK (keys -> dk_usable + keys -> dk_nentries <= usable );
697
+ CHECK (0 <= dk_usable && dk_usable <= usable );
698
+ CHECK (0 <= dk_nentries && dk_nentries <= usable );
699
+ CHECK (dk_usable + dk_nentries <= usable );
696
700
697
701
if (!splitted ) {
698
702
/* combined table */
@@ -709,6 +713,7 @@ _PyDict_CheckConsistency(PyObject *op, int check_content)
709
713
}
710
714
711
715
if (check_content ) {
716
+ LOCK_KEYS_IF_SPLIT (keys , keys -> dk_kind );
712
717
for (Py_ssize_t i = 0 ; i < DK_SIZE (keys ); i ++ ) {
713
718
Py_ssize_t ix = dictkeys_get_index (keys , i );
714
719
CHECK (DKIX_DUMMY <= ix && ix <= usable );
@@ -764,6 +769,7 @@ _PyDict_CheckConsistency(PyObject *op, int check_content)
764
769
CHECK (mp -> ma_values -> values [index ] != NULL );
765
770
}
766
771
}
772
+ UNLOCK_KEYS_IF_SPLIT (keys , keys -> dk_kind );
767
773
}
768
774
return 1 ;
769
775
@@ -4032,7 +4038,7 @@ dict_equal_lock_held(PyDictObject *a, PyDictObject *b)
4032
4038
/* can't be equal if # of entries differ */
4033
4039
return 0 ;
4034
4040
/* Same # of entries -- check all of 'em. Exit early on any diff. */
4035
- for (i = 0 ; i < LOAD_KEYS_NENTIRES (a -> ma_keys ); i ++ ) {
4041
+ for (i = 0 ; i < LOAD_KEYS_NENTRIES (a -> ma_keys ); i ++ ) {
4036
4042
PyObject * key , * aval ;
4037
4043
Py_hash_t hash ;
4038
4044
if (DK_IS_UNICODE (a -> ma_keys )) {
0 commit comments