Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Small cleanups
  • Loading branch information
DinoV committed Feb 19, 2025
commit 71ddb723d7eb733af76d0f3e9c07e30b73332b61
107 changes: 57 additions & 50 deletions Objects/dictobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -7163,6 +7163,17 @@ PyObject_VisitManagedDict(PyObject *obj, visitproc visit, void *arg)
return 0;
}

static void
clear_inline_values(PyDictValues *values)
{
if (values->valid) {
values->valid = 0;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need to be FT_ATOMIC_STORE_UINT8(values->valid, 0) for the call from set_dict_inline_values?

for (Py_ssize_t i = 0; i < values->capacity; i++) {
Py_CLEAR(values->values[i]);
}
}
}

static void
set_dict_inline_values(PyObject *obj, PyDictObject *new_dict)
{
Expand All @@ -7173,12 +7184,7 @@ set_dict_inline_values(PyObject *obj, PyDictObject *new_dict)
Py_XINCREF(new_dict);
FT_ATOMIC_STORE_PTR(_PyObject_ManagedDictPointer(obj)->dict, new_dict);

if (values->valid) {
FT_ATOMIC_STORE_UINT8(values->valid, 0);
for (Py_ssize_t i = 0; i < values->capacity; i++) {
Py_CLEAR(values->values[i]);
}
}
clear_inline_values(values);
}

#ifdef Py_GIL_DISABLED
Expand Down Expand Up @@ -7338,89 +7344,90 @@ _PyObject_SetManagedDict(PyObject *obj, PyObject *new_dict)
return err;
}

static int
detach_dict_from_object(PyDictObject *mp, PyObject *obj)
{
assert(_PyObject_ManagedDictPointer(obj)->dict == mp);
assert(_PyObject_InlineValuesConsistencyCheck(obj));

if (FT_ATOMIC_LOAD_PTR_RELAXED(mp->ma_values) != _PyObject_InlineValues(obj)) {
return 0;
}

// We could be called with an unlocked dict when the caller knows the
// values are already detached, so we assert after inline values check.
ASSERT_WORLD_STOPPED_OR_OBJ_LOCKED(mp);
assert(mp->ma_values->embedded == 1);
assert(mp->ma_values->valid == 1);
assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_INLINE_VALUES);

PyDictValues *values = copy_values(mp->ma_values);

if (values == NULL) {
PyErr_NoMemory();
return -1;
}
mp->ma_values = values;

invalidate_and_clear_inline_values(_PyObject_InlineValues(obj));

assert(_PyObject_InlineValuesConsistencyCheck(obj));
ASSERT_CONSISTENT(mp);
return 0;
}


void
PyObject_ClearManagedDict(PyObject *obj)
{
// This is called when the object is being freed and therefore
// has no other references or during GC when the world is stopped
// so we don't need any locking.
// This is called when the object is being freed or cleared
// by the GC and therefore known to have no references.
if (Py_TYPE(obj)->tp_flags & Py_TPFLAGS_INLINE_VALUES) {
PyDictObject *dict = _PyObject_GetManagedDict(obj);
if (dict == NULL) {
// We have no materialized dictionary and inline values
// that just need to be cleared.
PyDictValues *values = _PyObject_InlineValues(obj);
if (values->valid) {
values->valid = 0;
for (Py_ssize_t i = 0; i < values->capacity; i++) {
Py_CLEAR(values->values[i]);
}
}
// No dict to clear, we're done
clear_inline_values(_PyObject_InlineValues(obj));
return;
} else if (FT_ATOMIC_LOAD_PTR_RELAXED(dict->ma_values) ==
}
else if (FT_ATOMIC_LOAD_PTR_RELAXED(dict->ma_values) ==
_PyObject_InlineValues(obj)) {
// We have a materialized object which points at the inline
// values. We need to materialize the keys. Nothing can modify
// this object, but we need to lock the dictionary.
int err;
Py_BEGIN_CRITICAL_SECTION(dict);
err = _PyDict_DetachFromObject(dict, obj);
err = detach_dict_from_object(dict, obj);
Py_END_CRITICAL_SECTION();

if (err) {
/* Must be out of memory */
assert(PyErr_Occurred() == PyExc_MemoryError);
PyErr_FormatUnraisable("Exception ignored while "
"clearing an object managed dict");
"clearing an object managed dict");
/* Clear the dict */
Py_BEGIN_CRITICAL_SECTION2(dict, obj);
Py_BEGIN_CRITICAL_SECTION(dict);
PyInterpreterState *interp = _PyInterpreterState_GET();
PyDictKeysObject *oldkeys = dict->ma_keys;
set_keys(dict, Py_EMPTY_KEYS);
dict->ma_values = NULL;
dictkeys_decref(interp, oldkeys, IS_DICT_SHARED(dict));
STORE_USED(dict, 0);
set_dict_inline_values(obj, NULL);
Py_END_CRITICAL_SECTION2();
clear_inline_values(_PyObject_InlineValues(obj));
Py_END_CRITICAL_SECTION();
}
}
// Else we have a materialized dict which doesn't point at the inline
// values, we can just clear it.
}
Py_CLEAR(_PyObject_ManagedDictPointer(obj)->dict);
}

int
_PyDict_DetachFromObject(PyDictObject *mp, PyObject *obj)
{
assert(_PyObject_ManagedDictPointer(obj)->dict == mp);
assert(_PyObject_InlineValuesConsistencyCheck(obj));

if (FT_ATOMIC_LOAD_PTR_RELAXED(mp->ma_values) != _PyObject_InlineValues(obj)) {
return 0;
}

// We could be called with an unlocked dict when the caller knows the
// values are already detached, so we assert after inline values check.
ASSERT_WORLD_STOPPED_OR_OBJ_LOCKED(mp);
assert(mp->ma_values->embedded == 1);
assert(mp->ma_values->valid == 1);
assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_INLINE_VALUES);

PyDictValues *values = copy_values(mp->ma_values);

if (values == NULL) {
PyErr_NoMemory();
return -1;
}
mp->ma_values = values;

invalidate_and_clear_inline_values(_PyObject_InlineValues(obj));
ASSERT_WORLD_STOPPED_OR_OBJ_LOCKED(obj);

assert(_PyObject_InlineValuesConsistencyCheck(obj));
ASSERT_CONSISTENT(mp);
return 0;
return detach_dict_from_object(mp, obj);
}

static inline PyObject *
Expand Down