@@ -154,6 +154,11 @@ ASSERT_DICT_LOCKED(PyObject *op)
154
154
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED (op );
155
155
}
156
156
#define ASSERT_DICT_LOCKED (op ) ASSERT_DICT_LOCKED(_Py_CAST(PyObject*, op))
157
+ #define ASSERT_WORLD_STOPPED_OR_DICT_LOCKED (op ) \
158
+ if (!_PyInterpreterState_GET()->stoptheworld.world_stopped) { \
159
+ ASSERT_DICT_LOCKED(op); \
160
+ }
161
+
157
162
#define IS_DICT_SHARED (mp ) _PyObject_GC_IS_SHARED(mp)
158
163
#define SET_DICT_SHARED (mp ) _PyObject_GC_SET_SHARED(mp)
159
164
#define LOAD_INDEX (keys , size , idx ) _Py_atomic_load_int##size##_relaxed(&((const int##size##_t*)keys->dk_indices)[idx]);
@@ -670,6 +675,8 @@ dump_entries(PyDictKeysObject *dk)
670
675
int
671
676
_PyDict_CheckConsistency (PyObject * op , int check_content )
672
677
{
678
+ ASSERT_WORLD_STOPPED_OR_DICT_LOCKED (op );
679
+
673
680
#define CHECK (expr ) \
674
681
do { if (!(expr)) { _PyObject_ASSERT_FAILED_MSG(op, Py_STRINGIFY(expr)); } } while (0)
675
682
@@ -1580,6 +1587,8 @@ _PyDict_MaybeUntrack(PyObject *op)
1580
1587
PyObject * value ;
1581
1588
Py_ssize_t i , numentries ;
1582
1589
1590
+ ASSERT_WORLD_STOPPED_OR_DICT_LOCKED (op );
1591
+
1583
1592
if (!PyDict_CheckExact (op ) || !_PyObject_GC_IS_TRACKED (op ))
1584
1593
return ;
1585
1594
@@ -1722,13 +1731,14 @@ static void
1722
1731
insert_split_value (PyInterpreterState * interp , PyDictObject * mp , PyObject * key , PyObject * value , Py_ssize_t ix )
1723
1732
{
1724
1733
assert (PyUnicode_CheckExact (key ));
1734
+ ASSERT_DICT_LOCKED (mp );
1725
1735
MAINTAIN_TRACKING (mp , key , value );
1726
1736
PyObject * old_value = mp -> ma_values -> values [ix ];
1727
1737
if (old_value == NULL ) {
1728
1738
uint64_t new_version = _PyDict_NotifyEvent (interp , PyDict_EVENT_ADDED , mp , key , value );
1729
1739
STORE_SPLIT_VALUE (mp , ix , Py_NewRef (value ));
1730
1740
_PyDictValues_AddToInsertionOrder (mp -> ma_values , ix );
1731
- mp -> ma_used ++ ;
1741
+ STORE_USED ( mp , mp -> ma_used + 1 ) ;
1732
1742
mp -> ma_version_tag = new_version ;
1733
1743
}
1734
1744
else {
@@ -1792,7 +1802,7 @@ insertdict(PyInterpreterState *interp, PyDictObject *mp,
1792
1802
goto Fail ;
1793
1803
}
1794
1804
mp -> ma_version_tag = new_version ;
1795
- mp -> ma_used ++ ;
1805
+ STORE_USED ( mp , mp -> ma_used + 1 ) ;
1796
1806
ASSERT_CONSISTENT (mp );
1797
1807
return 0 ;
1798
1808
}
@@ -1861,7 +1871,7 @@ insert_to_emptydict(PyInterpreterState *interp, PyDictObject *mp,
1861
1871
ep -> me_hash = hash ;
1862
1872
STORE_VALUE (ep , value );
1863
1873
}
1864
- FT_ATOMIC_STORE_SSIZE_RELAXED (mp -> ma_used , FT_ATOMIC_LOAD_SSIZE_RELAXED ( mp -> ma_used ) + 1 );
1874
+ STORE_USED (mp , mp -> ma_used + 1 );
1865
1875
mp -> ma_version_tag = new_version ;
1866
1876
newkeys -> dk_usable -- ;
1867
1877
newkeys -> dk_nentries ++ ;
@@ -1870,11 +1880,7 @@ insert_to_emptydict(PyInterpreterState *interp, PyDictObject *mp,
1870
1880
// the case where we're inserting from the non-owner thread. We don't use
1871
1881
// set_keys here because the transition from empty to non-empty is safe
1872
1882
// as the empty keys will never be freed.
1873
- #ifdef Py_GIL_DISABLED
1874
- _Py_atomic_store_ptr_release (& mp -> ma_keys , newkeys );
1875
- #else
1876
- mp -> ma_keys = newkeys ;
1877
- #endif
1883
+ FT_ATOMIC_STORE_PTR_RELEASE (mp -> ma_keys , newkeys );
1878
1884
return 0 ;
1879
1885
}
1880
1886
@@ -2580,7 +2586,7 @@ delitem_common(PyDictObject *mp, Py_hash_t hash, Py_ssize_t ix,
2580
2586
Py_ssize_t hashpos = lookdict_index (mp -> ma_keys , hash , ix );
2581
2587
assert (hashpos >= 0 );
2582
2588
2583
- FT_ATOMIC_STORE_SSIZE_RELAXED (mp -> ma_used , FT_ATOMIC_LOAD_SSIZE ( mp -> ma_used ) - 1 );
2589
+ STORE_USED (mp , mp -> ma_used - 1 );
2584
2590
mp -> ma_version_tag = new_version ;
2585
2591
if (_PyDict_HasSplitTable (mp )) {
2586
2592
assert (old_value == mp -> ma_values -> values [ix ]);
@@ -2752,7 +2758,7 @@ clear_lock_held(PyObject *op)
2752
2758
// We don't inc ref empty keys because they're immortal
2753
2759
ensure_shared_on_resize (mp );
2754
2760
mp -> ma_version_tag = new_version ;
2755
- mp -> ma_used = 0 ;
2761
+ STORE_USED ( mp , 0 ) ;
2756
2762
if (oldvalues == NULL ) {
2757
2763
set_keys (mp , Py_EMPTY_KEYS );
2758
2764
assert (oldkeys -> dk_refcnt == 1 );
@@ -3191,6 +3197,8 @@ dict_repr_lock_held(PyObject *self)
3191
3197
_PyUnicodeWriter writer ;
3192
3198
int first ;
3193
3199
3200
+ ASSERT_DICT_LOCKED (mp );
3201
+
3194
3202
i = Py_ReprEnter ((PyObject * )mp );
3195
3203
if (i != 0 ) {
3196
3204
return i > 0 ? PyUnicode_FromString ("{...}" ) : NULL ;
@@ -3279,8 +3287,7 @@ dict_repr(PyObject *self)
3279
3287
static Py_ssize_t
3280
3288
dict_length (PyObject * self )
3281
3289
{
3282
- PyDictObject * mp = (PyDictObject * )self ;
3283
- return _Py_atomic_load_ssize_relaxed (& mp -> ma_used );
3290
+ return FT_ATOMIC_LOAD_SSIZE_RELAXED (((PyDictObject * )self )-> ma_used );
3284
3291
}
3285
3292
3286
3293
static PyObject *
@@ -3672,6 +3679,9 @@ PyDict_MergeFromSeq2(PyObject *d, PyObject *seq2, int override)
3672
3679
static int
3673
3680
dict_dict_merge (PyInterpreterState * interp , PyDictObject * mp , PyDictObject * other , int override )
3674
3681
{
3682
+ ASSERT_DICT_LOCKED (mp );
3683
+ ASSERT_DICT_LOCKED (other );
3684
+
3675
3685
if (other == mp || other -> ma_used == 0 )
3676
3686
/* a.update(a) or a.update({}); nothing to do */
3677
3687
return 0 ;
@@ -3699,7 +3709,7 @@ dict_dict_merge(PyInterpreterState *interp, PyDictObject *mp, PyDictObject *othe
3699
3709
ensure_shared_on_resize (mp );
3700
3710
dictkeys_decref (interp , mp -> ma_keys , IS_DICT_SHARED (mp ));
3701
3711
mp -> ma_keys = keys ;
3702
- mp -> ma_used = other -> ma_used ;
3712
+ STORE_USED ( mp , other -> ma_used ) ;
3703
3713
mp -> ma_version_tag = new_version ;
3704
3714
ASSERT_CONSISTENT (mp );
3705
3715
@@ -4034,7 +4044,7 @@ PyDict_Size(PyObject *mp)
4034
4044
PyErr_BadInternalCall ();
4035
4045
return -1 ;
4036
4046
}
4037
- return (( PyDictObject * )mp )-> ma_used ;
4047
+ return FT_ATOMIC_LOAD_SSIZE_RELAXED ((( PyDictObject * )mp )-> ma_used ) ;
4038
4048
}
4039
4049
4040
4050
/* Return 1 if dicts equal, 0 if not, -1 if error.
@@ -4291,7 +4301,7 @@ dict_setdefault_ref_lock_held(PyObject *d, PyObject *key, PyObject *default_valu
4291
4301
}
4292
4302
4293
4303
MAINTAIN_TRACKING (mp , key , value );
4294
- mp -> ma_used ++ ;
4304
+ STORE_USED ( mp , mp -> ma_used + 1 ) ;
4295
4305
mp -> ma_version_tag = new_version ;
4296
4306
assert (mp -> ma_keys -> dk_usable >= 0 );
4297
4307
ASSERT_CONSISTENT (mp );
@@ -4413,6 +4423,8 @@ dict_popitem_impl(PyDictObject *self)
4413
4423
uint64_t new_version ;
4414
4424
PyInterpreterState * interp = _PyInterpreterState_GET ();
4415
4425
4426
+ ASSERT_DICT_LOCKED (self );
4427
+
4416
4428
/* Allocate the result tuple before checking the size. Believe it
4417
4429
* or not, this allocation could trigger a garbage collection which
4418
4430
* could empty the dict, so if we checked the size first and that
@@ -4952,19 +4964,21 @@ typedef struct {
4952
4964
static PyObject *
4953
4965
dictiter_new (PyDictObject * dict , PyTypeObject * itertype )
4954
4966
{
4967
+ Py_ssize_t used ;
4955
4968
dictiterobject * di ;
4956
4969
di = PyObject_GC_New (dictiterobject , itertype );
4957
4970
if (di == NULL ) {
4958
4971
return NULL ;
4959
4972
}
4960
4973
di -> di_dict = (PyDictObject * )Py_NewRef (dict );
4961
- di -> di_used = dict -> ma_used ;
4962
- di -> len = dict -> ma_used ;
4974
+ used = FT_ATOMIC_LOAD_SSIZE (dict -> ma_used );
4975
+ di -> di_used = used ;
4976
+ di -> len = used ;
4963
4977
if (itertype == & PyDictRevIterKey_Type ||
4964
4978
itertype == & PyDictRevIterItem_Type ||
4965
4979
itertype == & PyDictRevIterValue_Type ) {
4966
4980
if (_PyDict_HasSplitTable (dict )) {
4967
- di -> di_pos = dict -> ma_used - 1 ;
4981
+ di -> di_pos = used - 1 ;
4968
4982
}
4969
4983
else {
4970
4984
di -> di_pos = load_keys_nentries (dict ) - 1 ;
@@ -5013,8 +5027,8 @@ dictiter_len(PyObject *self, PyObject *Py_UNUSED(ignored))
5013
5027
{
5014
5028
dictiterobject * di = (dictiterobject * )self ;
5015
5029
Py_ssize_t len = 0 ;
5016
- if (di -> di_dict != NULL && di -> di_used == di -> di_dict -> ma_used )
5017
- len = di -> len ;
5030
+ if (di -> di_dict != NULL && di -> di_used == FT_ATOMIC_LOAD_SSIZE_RELAXED ( di -> di_dict -> ma_used ) )
5031
+ len = FT_ATOMIC_LOAD_SSIZE_RELAXED ( di -> len ) ;
5018
5032
return PyLong_FromSize_t (len );
5019
5033
}
5020
5034
@@ -5297,6 +5311,7 @@ dictiter_iternextitem_lock_held(PyDictObject *d, PyObject *self,
5297
5311
Py_ssize_t i ;
5298
5312
5299
5313
assert (PyDict_Check (d ));
5314
+ ASSERT_DICT_LOCKED (d );
5300
5315
5301
5316
if (di -> di_used != d -> ma_used ) {
5302
5317
PyErr_SetString (PyExc_RuntimeError ,
@@ -5811,7 +5826,7 @@ dictview_len(PyObject *self)
5811
5826
_PyDictViewObject * dv = (_PyDictViewObject * )self ;
5812
5827
Py_ssize_t len = 0 ;
5813
5828
if (dv -> dv_dict != NULL )
5814
- len = dv -> dv_dict -> ma_used ;
5829
+ len = FT_ATOMIC_LOAD_SSIZE_RELAXED ( dv -> dv_dict -> ma_used ) ;
5815
5830
return len ;
5816
5831
}
5817
5832
@@ -6820,15 +6835,15 @@ store_instance_attr_lock_held(PyObject *obj, PyDictValues *values,
6820
6835
_PyDictValues_AddToInsertionOrder (values , ix );
6821
6836
if (dict ) {
6822
6837
assert (dict -> ma_values == values );
6823
- dict -> ma_used ++ ;
6838
+ STORE_USED ( dict , dict -> ma_used + 1 ) ;
6824
6839
}
6825
6840
}
6826
6841
else {
6827
6842
if (value == NULL ) {
6828
6843
delete_index_from_values (values , ix );
6829
6844
if (dict ) {
6830
6845
assert (dict -> ma_values == values );
6831
- dict -> ma_used -- ;
6846
+ STORE_USED ( dict , dict -> ma_used - 1 ) ;
6832
6847
}
6833
6848
}
6834
6849
Py_DECREF (old_value );
@@ -7039,7 +7054,7 @@ _PyObject_IsInstanceDictEmpty(PyObject *obj)
7039
7054
if (dict == NULL ) {
7040
7055
return 1 ;
7041
7056
}
7042
- return (( PyDictObject * )dict )-> ma_used == 0 ;
7057
+ return FT_ATOMIC_LOAD_SSIZE_RELAXED ((( PyDictObject * )dict )-> ma_used ) == 0 ;
7043
7058
}
7044
7059
7045
7060
int
0 commit comments