Skip to content

Commit fc7d1a4

Browse files
committed
Handle dict replacement case better
1 parent 319f632 commit fc7d1a4

File tree

1 file changed

+12
-16
lines changed

1 file changed

+12
-16
lines changed

Objects/dictobject.c

+12-16
Original file line numberDiff line numberDiff line change
@@ -7048,17 +7048,9 @@ _PyObject_SetManagedDict(PyObject *obj, PyObject *new_dict)
70487048

70497049
Py_BEGIN_CRITICAL_SECTION2(dict, obj);
70507050

7051-
#ifdef Py_DEBUG
7052-
// If the dict in the object has been replaced between when we got
7053-
// the dict and unlocked the objects then it's definitely no longer
7054-
// inline and there's no need to detach it, we can just replace it.
7055-
// The call to _PyDict_DetachFromObject will be a nop.
7056-
PyDictObject *cur_dict = _PyObject_ManagedDictPointer(obj)->dict;
7057-
assert(cur_dict == dict ||
7058-
(cur_dict->ma_values != _PyObject_InlineValues(obj) &&
7059-
dict->ma_values != _PyObject_InlineValues(obj) &&
7060-
!_PyObject_InlineValues(obj)->valid));
7061-
#endif
7051+
// We've locked dict, but the actual dict could have changed
7052+
// since we locked it.
7053+
dict = _PyObject_ManagedDictPointer(obj)->dict;
70627054

70637055
FT_ATOMIC_STORE_PTR(_PyObject_ManagedDictPointer(obj)->dict,
70647056
(PyDictObject *)Py_XNewRef(new_dict));
@@ -7067,7 +7059,7 @@ _PyObject_SetManagedDict(PyObject *obj, PyObject *new_dict)
70677059

70687060
Py_END_CRITICAL_SECTION2();
70697061

7070-
Py_DECREF(dict);
7062+
Py_XDECREF(dict);
70717063
}
70727064
else {
70737065
PyDictObject *dict;
@@ -7095,21 +7087,25 @@ PyObject_ClearManagedDict(PyObject *obj)
70957087
int
70967088
_PyDict_DetachFromObject(PyDictObject *mp, PyObject *obj)
70977089
{
7098-
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(mp);
70997090
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(obj);
71007091

7101-
if (mp->ma_values == NULL || mp->ma_values != _PyObject_InlineValues(obj)) {
7092+
if (FT_ATOMIC_LOAD_PTR_RELAXED(mp->ma_values) != _PyObject_InlineValues(obj)) {
71027093
return 0;
71037094
}
7095+
7096+
// We could be called with an unlocked dict when the caller knows the
7097+
// values are already detached, so we assert after inline values check.
7098+
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(mp);
71047099
assert(mp->ma_values->embedded == 1);
71057100
assert(mp->ma_values->valid == 1);
71067101
assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_INLINE_VALUES);
71077102

7108-
mp->ma_values = copy_values(mp->ma_values);
7103+
PyDictValues *values = copy_values(mp->ma_values);
71097104

7110-
if (mp->ma_values == NULL) {
7105+
if (values == NULL) {
71117106
return -1;
71127107
}
7108+
mp->ma_values = values;
71137109

71147110
FT_ATOMIC_STORE_UINT8(_PyObject_InlineValues(obj)->valid, 0);
71157111

0 commit comments

Comments
 (0)