@@ -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]);
@@ -221,6 +226,7 @@ static inline void split_keys_entry_added(PyDictKeysObject *keys)
221
226
#else /* Py_GIL_DISABLED */
222
227
223
228
#define ASSERT_DICT_LOCKED (op )
229
+ #define ASSERT_WORLD_STOPPED_OR_DICT_LOCKED (op )
224
230
#define LOCK_KEYS (keys )
225
231
#define UNLOCK_KEYS (keys )
226
232
#define ASSERT_KEYS_LOCKED (keys )
@@ -473,7 +479,7 @@ dictkeys_decref(PyInterpreterState *interp, PyDictKeysObject *dk, bool use_qsbr)
473
479
if (FT_ATOMIC_LOAD_SSIZE_RELAXED (dk -> dk_refcnt ) == _Py_IMMORTAL_REFCNT ) {
474
480
return ;
475
481
}
476
- assert (dk -> dk_refcnt > 0 );
482
+ assert (FT_ATOMIC_LOAD_SSIZE ( dk -> dk_refcnt ) > 0 );
477
483
#ifdef Py_REF_DEBUG
478
484
_Py_DecRefTotal (_PyThreadState_GET ());
479
485
#endif
@@ -670,6 +676,8 @@ dump_entries(PyDictKeysObject *dk)
670
676
int
671
677
_PyDict_CheckConsistency (PyObject * op , int check_content )
672
678
{
679
+ ASSERT_WORLD_STOPPED_OR_DICT_LOCKED (op );
680
+
673
681
#define CHECK (expr ) \
674
682
do { if (!(expr)) { _PyObject_ASSERT_FAILED_MSG(op, Py_STRINGIFY(expr)); } } while (0)
675
683
@@ -1580,6 +1588,8 @@ _PyDict_MaybeUntrack(PyObject *op)
1580
1588
PyObject * value ;
1581
1589
Py_ssize_t i , numentries ;
1582
1590
1591
+ ASSERT_WORLD_STOPPED_OR_DICT_LOCKED (op );
1592
+
1583
1593
if (!PyDict_CheckExact (op ) || !_PyObject_GC_IS_TRACKED (op ))
1584
1594
return ;
1585
1595
@@ -1722,13 +1732,14 @@ static void
1722
1732
insert_split_value (PyInterpreterState * interp , PyDictObject * mp , PyObject * key , PyObject * value , Py_ssize_t ix )
1723
1733
{
1724
1734
assert (PyUnicode_CheckExact (key ));
1735
+ ASSERT_DICT_LOCKED (mp );
1725
1736
MAINTAIN_TRACKING (mp , key , value );
1726
1737
PyObject * old_value = mp -> ma_values -> values [ix ];
1727
1738
if (old_value == NULL ) {
1728
1739
uint64_t new_version = _PyDict_NotifyEvent (interp , PyDict_EVENT_ADDED , mp , key , value );
1729
1740
STORE_SPLIT_VALUE (mp , ix , Py_NewRef (value ));
1730
1741
_PyDictValues_AddToInsertionOrder (mp -> ma_values , ix );
1731
- mp -> ma_used ++ ;
1742
+ STORE_USED ( mp , mp -> ma_used + 1 ) ;
1732
1743
mp -> ma_version_tag = new_version ;
1733
1744
}
1734
1745
else {
@@ -1792,7 +1803,7 @@ insertdict(PyInterpreterState *interp, PyDictObject *mp,
1792
1803
goto Fail ;
1793
1804
}
1794
1805
mp -> ma_version_tag = new_version ;
1795
- mp -> ma_used ++ ;
1806
+ STORE_USED ( mp , mp -> ma_used + 1 ) ;
1796
1807
ASSERT_CONSISTENT (mp );
1797
1808
return 0 ;
1798
1809
}
@@ -1861,7 +1872,7 @@ insert_to_emptydict(PyInterpreterState *interp, PyDictObject *mp,
1861
1872
ep -> me_hash = hash ;
1862
1873
STORE_VALUE (ep , value );
1863
1874
}
1864
- FT_ATOMIC_STORE_SSIZE_RELAXED (mp -> ma_used , FT_ATOMIC_LOAD_SSIZE_RELAXED ( mp -> ma_used ) + 1 );
1875
+ STORE_USED (mp , mp -> ma_used + 1 );
1865
1876
mp -> ma_version_tag = new_version ;
1866
1877
newkeys -> dk_usable -- ;
1867
1878
newkeys -> dk_nentries ++ ;
@@ -1870,11 +1881,7 @@ insert_to_emptydict(PyInterpreterState *interp, PyDictObject *mp,
1870
1881
// the case where we're inserting from the non-owner thread. We don't use
1871
1882
// set_keys here because the transition from empty to non-empty is safe
1872
1883
// 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
1884
+ FT_ATOMIC_STORE_PTR_RELEASE (mp -> ma_keys , newkeys );
1878
1885
return 0 ;
1879
1886
}
1880
1887
@@ -2560,7 +2567,7 @@ delitem_common(PyDictObject *mp, Py_hash_t hash, Py_ssize_t ix,
2560
2567
Py_ssize_t hashpos = lookdict_index (mp -> ma_keys , hash , ix );
2561
2568
assert (hashpos >= 0 );
2562
2569
2563
- FT_ATOMIC_STORE_SSIZE_RELAXED (mp -> ma_used , FT_ATOMIC_LOAD_SSIZE ( mp -> ma_used ) - 1 );
2570
+ STORE_USED (mp , mp -> ma_used - 1 );
2564
2571
mp -> ma_version_tag = new_version ;
2565
2572
if (_PyDict_HasSplitTable (mp )) {
2566
2573
assert (old_value == mp -> ma_values -> values [ix ]);
@@ -2730,7 +2737,7 @@ clear_lock_held(PyObject *op)
2730
2737
// We don't inc ref empty keys because they're immortal
2731
2738
ensure_shared_on_resize (mp );
2732
2739
mp -> ma_version_tag = new_version ;
2733
- mp -> ma_used = 0 ;
2740
+ STORE_USED ( mp , 0 ) ;
2734
2741
if (oldvalues == NULL ) {
2735
2742
set_keys (mp , Py_EMPTY_KEYS );
2736
2743
assert (oldkeys -> dk_refcnt == 1 );
@@ -3160,6 +3167,8 @@ dict_repr_lock_held(PyObject *self)
3160
3167
_PyUnicodeWriter writer ;
3161
3168
int first ;
3162
3169
3170
+ ASSERT_DICT_LOCKED (mp );
3171
+
3163
3172
i = Py_ReprEnter ((PyObject * )mp );
3164
3173
if (i != 0 ) {
3165
3174
return i > 0 ? PyUnicode_FromString ("{...}" ) : NULL ;
@@ -3248,8 +3257,7 @@ dict_repr(PyObject *self)
3248
3257
static Py_ssize_t
3249
3258
dict_length (PyObject * self )
3250
3259
{
3251
- PyDictObject * mp = (PyDictObject * )self ;
3252
- return _Py_atomic_load_ssize_relaxed (& mp -> ma_used );
3260
+ return FT_ATOMIC_LOAD_SSIZE_RELAXED (((PyDictObject * )self )-> ma_used );
3253
3261
}
3254
3262
3255
3263
static PyObject *
@@ -3640,6 +3648,9 @@ PyDict_MergeFromSeq2(PyObject *d, PyObject *seq2, int override)
3640
3648
static int
3641
3649
dict_dict_merge (PyInterpreterState * interp , PyDictObject * mp , PyDictObject * other , int override )
3642
3650
{
3651
+ ASSERT_DICT_LOCKED (mp );
3652
+ ASSERT_DICT_LOCKED (other );
3653
+
3643
3654
if (other == mp || other -> ma_used == 0 )
3644
3655
/* a.update(a) or a.update({}); nothing to do */
3645
3656
return 0 ;
@@ -3667,7 +3678,7 @@ dict_dict_merge(PyInterpreterState *interp, PyDictObject *mp, PyDictObject *othe
3667
3678
ensure_shared_on_resize (mp );
3668
3679
dictkeys_decref (interp , mp -> ma_keys , IS_DICT_SHARED (mp ));
3669
3680
mp -> ma_keys = keys ;
3670
- mp -> ma_used = other -> ma_used ;
3681
+ STORE_USED ( mp , other -> ma_used ) ;
3671
3682
mp -> ma_version_tag = new_version ;
3672
3683
ASSERT_CONSISTENT (mp );
3673
3684
@@ -4002,7 +4013,7 @@ PyDict_Size(PyObject *mp)
4002
4013
PyErr_BadInternalCall ();
4003
4014
return -1 ;
4004
4015
}
4005
- return (( PyDictObject * )mp )-> ma_used ;
4016
+ return FT_ATOMIC_LOAD_SSIZE_RELAXED ((( PyDictObject * )mp )-> ma_used ) ;
4006
4017
}
4007
4018
4008
4019
/* Return 1 if dicts equal, 0 if not, -1 if error.
@@ -4256,7 +4267,7 @@ dict_setdefault_ref_lock_held(PyObject *d, PyObject *key, PyObject *default_valu
4256
4267
}
4257
4268
4258
4269
MAINTAIN_TRACKING (mp , key , value );
4259
- mp -> ma_used ++ ;
4270
+ STORE_USED ( mp , mp -> ma_used + 1 ) ;
4260
4271
mp -> ma_version_tag = new_version ;
4261
4272
assert (mp -> ma_keys -> dk_usable >= 0 );
4262
4273
ASSERT_CONSISTENT (mp );
@@ -4378,6 +4389,8 @@ dict_popitem_impl(PyDictObject *self)
4378
4389
uint64_t new_version ;
4379
4390
PyInterpreterState * interp = _PyInterpreterState_GET ();
4380
4391
4392
+ ASSERT_DICT_LOCKED (self );
4393
+
4381
4394
/* Allocate the result tuple before checking the size. Believe it
4382
4395
* or not, this allocation could trigger a garbage collection which
4383
4396
* could empty the dict, so if we checked the size first and that
@@ -4916,19 +4929,21 @@ typedef struct {
4916
4929
static PyObject *
4917
4930
dictiter_new (PyDictObject * dict , PyTypeObject * itertype )
4918
4931
{
4932
+ Py_ssize_t used ;
4919
4933
dictiterobject * di ;
4920
4934
di = PyObject_GC_New (dictiterobject , itertype );
4921
4935
if (di == NULL ) {
4922
4936
return NULL ;
4923
4937
}
4924
4938
di -> di_dict = (PyDictObject * )Py_NewRef (dict );
4925
- di -> di_used = dict -> ma_used ;
4926
- di -> len = dict -> ma_used ;
4939
+ used = FT_ATOMIC_LOAD_SSIZE_RELAXED (dict -> ma_used );
4940
+ di -> di_used = used ;
4941
+ di -> len = used ;
4927
4942
if (itertype == & PyDictRevIterKey_Type ||
4928
4943
itertype == & PyDictRevIterItem_Type ||
4929
4944
itertype == & PyDictRevIterValue_Type ) {
4930
4945
if (_PyDict_HasSplitTable (dict )) {
4931
- di -> di_pos = dict -> ma_used - 1 ;
4946
+ di -> di_pos = used - 1 ;
4932
4947
}
4933
4948
else {
4934
4949
di -> di_pos = load_keys_nentries (dict ) - 1 ;
@@ -4977,8 +4992,8 @@ dictiter_len(PyObject *self, PyObject *Py_UNUSED(ignored))
4977
4992
{
4978
4993
dictiterobject * di = (dictiterobject * )self ;
4979
4994
Py_ssize_t len = 0 ;
4980
- if (di -> di_dict != NULL && di -> di_used == di -> di_dict -> ma_used )
4981
- len = di -> len ;
4995
+ if (di -> di_dict != NULL && di -> di_used == FT_ATOMIC_LOAD_SSIZE_RELAXED ( di -> di_dict -> ma_used ) )
4996
+ len = FT_ATOMIC_LOAD_SSIZE_RELAXED ( di -> len ) ;
4982
4997
return PyLong_FromSize_t (len );
4983
4998
}
4984
4999
@@ -5261,6 +5276,7 @@ dictiter_iternextitem_lock_held(PyDictObject *d, PyObject *self,
5261
5276
Py_ssize_t i ;
5262
5277
5263
5278
assert (PyDict_Check (d ));
5279
+ ASSERT_DICT_LOCKED (d );
5264
5280
5265
5281
if (di -> di_used != d -> ma_used ) {
5266
5282
PyErr_SetString (PyExc_RuntimeError ,
@@ -5775,7 +5791,7 @@ dictview_len(PyObject *self)
5775
5791
_PyDictViewObject * dv = (_PyDictViewObject * )self ;
5776
5792
Py_ssize_t len = 0 ;
5777
5793
if (dv -> dv_dict != NULL )
5778
- len = dv -> dv_dict -> ma_used ;
5794
+ len = FT_ATOMIC_LOAD_SSIZE_RELAXED ( dv -> dv_dict -> ma_used ) ;
5779
5795
return len ;
5780
5796
}
5781
5797
@@ -6782,15 +6798,15 @@ store_instance_attr_lock_held(PyObject *obj, PyDictValues *values,
6782
6798
_PyDictValues_AddToInsertionOrder (values , ix );
6783
6799
if (dict ) {
6784
6800
assert (dict -> ma_values == values );
6785
- dict -> ma_used ++ ;
6801
+ STORE_USED ( dict , dict -> ma_used + 1 ) ;
6786
6802
}
6787
6803
}
6788
6804
else {
6789
6805
if (value == NULL ) {
6790
6806
delete_index_from_values (values , ix );
6791
6807
if (dict ) {
6792
6808
assert (dict -> ma_values == values );
6793
- dict -> ma_used -- ;
6809
+ STORE_USED ( dict , dict -> ma_used - 1 ) ;
6794
6810
}
6795
6811
}
6796
6812
Py_DECREF (old_value );
@@ -7001,7 +7017,7 @@ _PyObject_IsInstanceDictEmpty(PyObject *obj)
7001
7017
if (dict == NULL ) {
7002
7018
return 1 ;
7003
7019
}
7004
- return (( PyDictObject * )dict )-> ma_used == 0 ;
7020
+ return FT_ATOMIC_LOAD_SSIZE_RELAXED ((( PyDictObject * )dict )-> ma_used ) == 0 ;
7005
7021
}
7006
7022
7007
7023
int
0 commit comments