@@ -169,16 +169,15 @@ ASSERT_DICT_LOCKED(PyObject *op)
169169#define STORE_INDEX (keys , size , idx , value ) _Py_atomic_store_int##size##_relaxed(&((int##size##_t*)keys->dk_indices)[idx], (int##size##_t)value);
170170#define ASSERT_OWNED_OR_SHARED (mp ) \
171171 assert(_Py_IsOwnedByCurrentThread((PyObject *)mp) || IS_DICT_SHARED(mp));
172- #define LOAD_KEYS_NENTRIES (d )
173172
174173#define LOCK_KEYS_IF_SPLIT (keys , kind ) \
175174 if (kind == DICT_KEYS_SPLIT) { \
176- LOCK_KEYS(dk); \
175+ LOCK_KEYS(keys); \
177176 }
178177
179178#define UNLOCK_KEYS_IF_SPLIT (keys , kind ) \
180179 if (kind == DICT_KEYS_SPLIT) { \
181- UNLOCK_KEYS(dk); \
180+ UNLOCK_KEYS(keys); \
182181 }
183182
184183static inline Py_ssize_t
@@ -212,7 +211,7 @@ set_values(PyDictObject *mp, PyDictValues *values)
212211#define INCREF_KEYS (dk ) _Py_atomic_add_ssize(&dk->dk_refcnt, 1)
213212// Dec refs the keys object, giving the previous value
214213#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)
216215
217216#define INCREF_KEYS_FT (dk ) dictkeys_incref(dk)
218217#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)
239238#define STORE_SHARED_KEY (key , value ) key = value
240239#define INCREF_KEYS (dk ) dk->dk_refcnt++
241240#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
243242#define INCREF_KEYS_FT (dk )
244243#define DECREF_KEYS_FT (dk , shared )
245244#define LOCK_KEYS_IF_SPLIT (keys , kind )
@@ -694,10 +693,15 @@ _PyDict_CheckConsistency(PyObject *op, int check_content)
694693 int splitted = _PyDict_HasSplitTable (mp );
695694 Py_ssize_t usable = USABLE_FRACTION (DK_SIZE (keys ));
696695
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+
697701 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 );
701705
702706 if (!splitted ) {
703707 /* combined table */
@@ -714,6 +718,7 @@ _PyDict_CheckConsistency(PyObject *op, int check_content)
714718 }
715719
716720 if (check_content ) {
721+ LOCK_KEYS_IF_SPLIT (keys , keys -> dk_kind );
717722 for (Py_ssize_t i = 0 ; i < DK_SIZE (keys ); i ++ ) {
718723 Py_ssize_t ix = dictkeys_get_index (keys , i );
719724 CHECK (DKIX_DUMMY <= ix && ix <= usable );
@@ -769,6 +774,7 @@ _PyDict_CheckConsistency(PyObject *op, int check_content)
769774 CHECK (mp -> ma_values -> values [index ] != NULL );
770775 }
771776 }
777+ UNLOCK_KEYS_IF_SPLIT (keys , keys -> dk_kind );
772778 }
773779 return 1 ;
774780
@@ -4037,7 +4043,7 @@ dict_equal_lock_held(PyDictObject *a, PyDictObject *b)
40374043 /* can't be equal if # of entries differ */
40384044 return 0 ;
40394045 /* 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 ++ ) {
40414047 PyObject * key , * aval ;
40424048 Py_hash_t hash ;
40434049 if (DK_IS_UNICODE (a -> ma_keys )) {
0 commit comments