@@ -282,6 +282,24 @@ load_keys_nentries(PyDictObject *mp)
282282
283283#endif
284284
285+ #ifndef NDEBUG
286+ // Check if it's possible to modify a dictionary.
287+ // Usage: assert(can_modify_dict(mp)).
288+ static inline int
289+ can_modify_dict (PyDictObject * mp )
290+ {
291+ if (PyFrozenDict_Check (mp )) {
292+ // No locking required to modify a newly created frozendict
293+ // since it's only accessible from the current thread.
294+ return PyUnstable_Object_IsUniquelyReferenced (_PyObject_CAST (mp ));
295+ }
296+ else {
297+ ASSERT_DICT_LOCKED (mp );
298+ return 1 ;
299+ }
300+ }
301+ #endif
302+
285303#define _PyAnyDict_CAST (op ) \
286304 (assert(PyAnyDict_Check(op)), _Py_CAST(PyDictObject*, op))
287305
@@ -1867,8 +1885,9 @@ insert_split_key(PyDictKeysObject *keys, PyObject *key, Py_hash_t hash)
18671885static void
18681886insert_split_value (PyDictObject * mp , PyObject * key , PyObject * value , Py_ssize_t ix )
18691887{
1888+ assert (can_modify_dict (mp ));
18701889 assert (PyUnicode_CheckExact (key ));
1871- ASSERT_DICT_LOCKED ( mp );
1890+
18721891 PyObject * old_value = mp -> ma_values -> values [ix ];
18731892 if (old_value == NULL ) {
18741893 _PyDict_NotifyEvent (PyDict_EVENT_ADDED , mp , key , value );
@@ -1896,11 +1915,11 @@ static int
18961915insertdict (PyDictObject * mp ,
18971916 PyObject * key , Py_hash_t hash , PyObject * value )
18981917{
1918+ assert (can_modify_dict (mp ));
1919+
18991920 PyObject * old_value = NULL ;
19001921 Py_ssize_t ix ;
19011922
1902- ASSERT_DICT_LOCKED (mp );
1903-
19041923 if (_PyDict_HasSplitTable (mp ) && PyUnicode_CheckExact (key )) {
19051924 ix = insert_split_key (mp -> ma_keys , key , hash );
19061925 if (ix != DKIX_EMPTY ) {
@@ -1967,8 +1986,8 @@ static int
19671986insert_to_emptydict (PyDictObject * mp ,
19681987 PyObject * key , Py_hash_t hash , PyObject * value )
19691988{
1989+ assert (can_modify_dict (mp ));
19701990 assert (mp -> ma_keys == Py_EMPTY_KEYS );
1971- ASSERT_DICT_LOCKED (mp );
19721991
19731992 int unicode = PyUnicode_CheckExact (key );
19741993 PyDictKeysObject * newkeys = new_keys_object (PyDict_LOG_MINSIZE , unicode );
@@ -2069,11 +2088,11 @@ static int
20692088dictresize (PyDictObject * mp ,
20702089 uint8_t log2_newsize , int unicode )
20712090{
2091+ assert (can_modify_dict (mp ));
2092+
20722093 PyDictKeysObject * oldkeys , * newkeys ;
20732094 PyDictValues * oldvalues ;
20742095
2075- ASSERT_DICT_LOCKED (mp );
2076-
20772096 if (log2_newsize >= SIZEOF_SIZE_T * 8 ) {
20782097 PyErr_NoMemory ();
20792098 return -1 ;
@@ -2671,11 +2690,13 @@ _PyDict_LoadBuiltinsFromGlobals(PyObject *globals)
26712690
26722691/* Consumes references to key and value */
26732692static int
2674- anydict_setitem_take2 (PyDictObject * mp , PyObject * key , PyObject * value )
2693+ setitem_take2_lock_held (PyDictObject * mp , PyObject * key , PyObject * value )
26752694{
2695+ assert (PyAnyDict_Check (mp ));
2696+ assert (can_modify_dict (mp ));
26762697 assert (key );
26772698 assert (value );
2678- assert ( PyAnyDict_Check ( mp ));
2699+
26792700 Py_hash_t hash = _PyObject_HashFast (key );
26802701 if (hash == -1 ) {
26812702 dict_unhashable_type (key );
@@ -2691,14 +2712,6 @@ anydict_setitem_take2(PyDictObject *mp, PyObject *key, PyObject *value)
26912712 return insertdict (mp , key , hash , value );
26922713}
26932714
2694- /* Consumes references to key and value */
2695- static int
2696- setitem_take2_lock_held (PyDictObject * mp , PyObject * key , PyObject * value )
2697- {
2698- ASSERT_DICT_LOCKED (mp );
2699- return anydict_setitem_take2 (mp , key , value );
2700- }
2701-
27022715int
27032716_PyDict_SetItem_Take2 (PyDictObject * mp , PyObject * key , PyObject * value )
27042717{
@@ -2800,9 +2813,9 @@ static void
28002813delitem_common (PyDictObject * mp , Py_hash_t hash , Py_ssize_t ix ,
28012814 PyObject * old_value )
28022815{
2803- PyObject * old_key ;
2816+ assert ( can_modify_dict ( mp )) ;
28042817
2805- ASSERT_DICT_LOCKED ( mp ) ;
2818+ PyObject * old_key ;
28062819
28072820 Py_ssize_t hashpos = lookdict_index (mp -> ma_keys , hash , ix );
28082821 assert (hashpos >= 0 );
@@ -2856,19 +2869,17 @@ int
28562869_PyDict_DelItem_KnownHash_LockHeld (PyObject * op , PyObject * key , Py_hash_t hash )
28572870{
28582871 Py_ssize_t ix ;
2859- PyDictObject * mp ;
28602872 PyObject * old_value ;
28612873
28622874 if (!PyDict_Check (op )) {
28632875 PyErr_BadInternalCall ();
28642876 return -1 ;
28652877 }
2866-
2867- ASSERT_DICT_LOCKED ( op );
2878+ PyDictObject * mp = ( PyDictObject * ) op ;
2879+ assert ( can_modify_dict ( mp ) );
28682880
28692881 assert (key );
28702882 assert (hash != -1 );
2871- mp = (PyDictObject * )op ;
28722883 ix = _Py_dict_lookup (mp , key , hash , & old_value );
28732884 if (ix == DKIX_ERROR )
28742885 return -1 ;
@@ -2897,19 +2908,18 @@ delitemif_lock_held(PyObject *op, PyObject *key,
28972908 int (* predicate )(PyObject * value , void * arg ),
28982909 void * arg )
28992910{
2911+ PyDictObject * mp = _PyAnyDict_CAST (op );
2912+ assert (can_modify_dict (mp ));
2913+
29002914 Py_ssize_t ix ;
2901- PyDictObject * mp ;
29022915 Py_hash_t hash ;
29032916 PyObject * old_value ;
29042917 int res ;
29052918
2906- ASSERT_DICT_LOCKED (op );
2907-
29082919 assert (key );
29092920 hash = PyObject_Hash (key );
29102921 if (hash == -1 )
29112922 return -1 ;
2912- mp = (PyDictObject * )op ;
29132923 ix = _Py_dict_lookup (mp , key , hash , & old_value );
29142924 if (ix == DKIX_ERROR ) {
29152925 return -1 ;
@@ -2951,16 +2961,16 @@ _PyDict_DelItemIf(PyObject *op, PyObject *key,
29512961static void
29522962clear_lock_held (PyObject * op )
29532963{
2954- PyDictObject * mp ;
2964+ if (!PyDict_Check (op )) {
2965+ return ;
2966+ }
2967+ PyDictObject * mp = (PyDictObject * )op ;
2968+ assert (can_modify_dict (mp ));
2969+
29552970 PyDictKeysObject * oldkeys ;
29562971 PyDictValues * oldvalues ;
29572972 Py_ssize_t i , n ;
29582973
2959- ASSERT_DICT_LOCKED (op );
2960-
2961- if (!PyDict_Check (op ))
2962- return ;
2963- mp = ((PyDictObject * )op );
29642974 oldkeys = mp -> ma_keys ;
29652975 oldvalues = mp -> ma_values ;
29662976 if (oldkeys == Py_EMPTY_KEYS ) {
@@ -3106,8 +3116,7 @@ _PyDict_Pop_KnownHash(PyDictObject *mp, PyObject *key, Py_hash_t hash,
31063116 PyObject * * result )
31073117{
31083118 assert (PyDict_Check (mp ));
3109-
3110- ASSERT_DICT_LOCKED (mp );
3119+ assert (can_modify_dict (mp ));
31113120
31123121 if (mp -> ma_used == 0 ) {
31133122 if (result ) {
@@ -3149,8 +3158,6 @@ _PyDict_Pop_KnownHash(PyDictObject *mp, PyObject *key, Py_hash_t hash,
31493158static int
31503159pop_lock_held (PyObject * op , PyObject * key , PyObject * * result )
31513160{
3152- ASSERT_DICT_LOCKED (op );
3153-
31543161 if (!PyDict_Check (op )) {
31553162 if (result ) {
31563163 * result = NULL ;
@@ -3159,6 +3166,7 @@ pop_lock_held(PyObject *op, PyObject *key, PyObject **result)
31593166 return -1 ;
31603167 }
31613168 PyDictObject * dict = (PyDictObject * )op ;
3169+ assert (can_modify_dict (dict ));
31623170
31633171 if (dict -> ma_used == 0 ) {
31643172 if (result ) {
@@ -3361,9 +3369,9 @@ dict_iter_exit:;
33613369 }
33623370 else if (PyFrozenDict_CheckExact (d )) {
33633371 while ((key = PyIter_Next (it )) != NULL ) {
3364- // anydict_setitem_take2 consumes a reference to key
3365- status = anydict_setitem_take2 ((PyDictObject * )d ,
3366- key , Py_NewRef (value ));
3372+ // setitem_take2_lock_held consumes a reference to key
3373+ status = setitem_take2_lock_held ((PyDictObject * )d ,
3374+ key , Py_NewRef (value ));
33673375 if (status < 0 ) {
33683376 assert (PyErr_Occurred ());
33693377 goto Fail ;
@@ -3933,7 +3941,7 @@ PyDict_MergeFromSeq2(PyObject *d, PyObject *seq2, int override)
39333941static int
39343942dict_dict_merge (PyDictObject * mp , PyDictObject * other , int override )
39353943{
3936- ASSERT_DICT_LOCKED ( mp );
3944+ assert ( can_modify_dict ( mp ) );
39373945 ASSERT_DICT_LOCKED (other );
39383946
39393947 if (other == mp || other -> ma_used == 0 )
@@ -4474,15 +4482,14 @@ dict_setdefault_ref_lock_held(PyObject *d, PyObject *key, PyObject *default_valu
44744482 Py_hash_t hash ;
44754483 Py_ssize_t ix ;
44764484
4477- ASSERT_DICT_LOCKED (d );
4478-
44794485 if (!PyDict_Check (d )) {
44804486 PyErr_BadInternalCall ();
44814487 if (result ) {
44824488 * result = NULL ;
44834489 }
44844490 return -1 ;
44854491 }
4492+ assert (can_modify_dict ((PyDictObject * )d ));
44864493
44874494 hash = _PyObject_HashFast (key );
44884495 if (hash == -1 ) {
@@ -4657,11 +4664,11 @@ static PyObject *
46574664dict_popitem_impl (PyDictObject * self )
46584665/*[clinic end generated code: output=e65fcb04420d230d input=ef28b4da5f0f762e]*/
46594666{
4667+ assert (can_modify_dict (self ));
4668+
46604669 Py_ssize_t i , j ;
46614670 PyObject * res ;
46624671
4663- ASSERT_DICT_LOCKED (self );
4664-
46654672 /* Allocate the result tuple before checking the size. Believe it
46664673 * or not, this allocation could trigger a garbage collection which
46674674 * could empty the dict, so if we checked the size first and that
@@ -4799,11 +4806,12 @@ _PyDict_SizeOf_LockHeld(PyDictObject *mp)
47994806}
48004807
48014808void
4802- _PyDict_ClearKeysVersionLockHeld (PyObject * mp )
4809+ _PyDict_ClearKeysVersionLockHeld (PyObject * op )
48034810{
4804- ASSERT_DICT_LOCKED (mp );
4811+ PyDictObject * mp = _PyAnyDict_CAST (op );
4812+ assert (can_modify_dict (mp ));
48054813
4806- FT_ATOMIC_STORE_UINT32_RELAXED ((( PyDictObject * ) mp ) -> ma_keys -> dk_version , 0 );
4814+ FT_ATOMIC_STORE_UINT32_RELAXED (mp -> ma_keys -> dk_version , 0 );
48074815}
48084816
48094817Py_ssize_t
0 commit comments