@@ -7150,7 +7150,8 @@ try_set_dict_inline_only_or_other_dict(PyObject *obj, PyObject *new_dict, PyDict
71507150 (PyDictObject * )Py_XNewRef (new_dict ));
71517151 replaced = true;
71527152 goto exit_lock ;
7153- } else {
7153+ }
7154+ else {
71547155 // We have inline values, we need to lock the dict and the object
71557156 // at the same time to safely dematerialize them. To do that while releasing
71567157 // the object lock we need a strong reference to the current dictionary.
@@ -7166,38 +7167,37 @@ try_set_dict_inline_only_or_other_dict(PyObject *obj, PyObject *new_dict, PyDict
71667167// and replaced it with another dictionary though.
71677168static int
71687169replace_dict_probably_inline_materialized (PyObject * obj , PyDictObject * inline_dict ,
7169- PyObject * new_dict , bool clear ,
7170- PyDictObject * * replaced_dict )
7170+ PyDictObject * cur_dict , PyObject * new_dict )
71717171{
7172- // But we could have had another thread race in after we released
7173- // the object lock
7174- int err = 0 ;
7175- * replaced_dict = _PyObject_GetManagedDict (obj );
7176- assert (FT_ATOMIC_LOAD_PTR_RELAXED (inline_dict -> ma_values ) == _PyObject_InlineValues (obj ));
7172+ _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED (obj );
7173+
7174+ if (cur_dict == inline_dict ) {
7175+ assert (FT_ATOMIC_LOAD_PTR_RELAXED (inline_dict -> ma_values ) == _PyObject_InlineValues (obj ));
71777176
7178- if (* replaced_dict == inline_dict ) {
7179- err = _PyDict_DetachFromObject (inline_dict , obj );
7177+ int err = _PyDict_DetachFromObject (inline_dict , obj );
71807178 if (err != 0 ) {
71817179 return err ;
71827180 }
7183- // We incref'd the inline dict and the object owns a ref.
7184- // Clear the object's reference, we'll clear the local
7185- // reference after releasing the lock.
7186- if (clear ) {
7187- Py_XDECREF ((PyObject * )* replaced_dict );
7188- } else {
7189- _PyObject_XDecRefDelayed ((PyObject * )* replaced_dict );
7190- }
7191- * replaced_dict = NULL ;
71927181 }
71937182
71947183 FT_ATOMIC_STORE_PTR (_PyObject_ManagedDictPointer (obj )-> dict ,
7195- (PyDictObject * )Py_XNewRef (new_dict ));
7196- return err ;
7184+ (PyDictObject * )Py_XNewRef (new_dict ));
7185+ return 0 ;
71977186}
71987187
71997188#endif
72007189
7190+ static void
7191+ decref_maybe_delay (PyObject * obj , bool delay )
7192+ {
7193+ if (delay ) {
7194+ _PyObject_XDecRefDelayed (obj );
7195+ }
7196+ else {
7197+ Py_XDECREF (obj );
7198+ }
7199+ }
7200+
72017201static int
72027202set_or_clear_managed_dict (PyObject * obj , PyObject * new_dict , bool clear )
72037203{
@@ -7213,32 +7213,37 @@ set_or_clear_managed_dict(PyObject *obj, PyObject *new_dict, bool clear)
72137213 // values. We need to lock both the object and the dict at the
72147214 // same time to safely replace it. We can't merely lock the dictionary
72157215 // while the object is locked because it could suspend the object lock.
7216- PyDictObject * replaced_dict ;
7216+ PyDictObject * cur_dict ;
72177217
72187218 assert (prev_dict != NULL );
72197219 Py_BEGIN_CRITICAL_SECTION2 (obj , prev_dict );
72207220
7221- err = replace_dict_probably_inline_materialized (obj , prev_dict , new_dict ,
7222- clear , & replaced_dict );
7221+ // We could have had another thread race in between the call to
7222+ // try_set_dict_inline_only_or_other_dict where we locked the object
7223+ // and when we unlocked and re-locked the dictionary.
7224+ cur_dict = _PyObject_GetManagedDict (obj );
7225+
7226+ err = replace_dict_probably_inline_materialized (obj , prev_dict ,
7227+ cur_dict , new_dict );
72237228
72247229 Py_END_CRITICAL_SECTION2 ();
72257230
7226- Py_DECREF (prev_dict );
7231+ // Decref for the dictionary we incref'd in try_set_dict_inline_only_or_other_dict
7232+ // while the object was locked
7233+ decref_maybe_delay ((PyObject * )prev_dict ,
7234+ !clear && prev_dict != cur_dict );
72277235 if (err != 0 ) {
72287236 return err ;
72297237 }
7230- prev_dict = replaced_dict ;
7238+
7239+ prev_dict = cur_dict ;
72317240 }
72327241
72337242 if (prev_dict != NULL ) {
7234- // Readers from the old dictionary use a borrowed reference. We need
7235- // to set the decref the dict at the next safe point.
7236- if (clear ) {
7237- Py_XDECREF ((PyObject * )prev_dict );
7238- } else {
7239- _PyObject_XDecRefDelayed ((PyObject * )prev_dict );
7240- }
7243+ // decref for the dictionary that we replaced
7244+ decref_maybe_delay ((PyObject * )prev_dict , !clear );
72417245 }
7246+
72427247 return 0 ;
72437248#else
72447249 PyDictObject * dict = _PyObject_GetManagedDict (obj );
@@ -7265,11 +7270,7 @@ set_or_clear_managed_dict(PyObject *obj, PyObject *new_dict, bool clear)
72657270 (PyDictObject * )Py_XNewRef (new_dict ));
72667271
72677272 Py_END_CRITICAL_SECTION ();
7268- if (clear ) {
7269- Py_XDECREF ((PyObject * )dict );
7270- } else {
7271- _PyObject_XDecRefDelayed ((PyObject * )dict );
7272- }
7273+ decref_maybe_delay ((PyObject * )dict , !clear );
72737274 }
72747275 assert (_PyObject_InlineValuesConsistencyCheck (obj ));
72757276 return err ;
0 commit comments