Skip to content
Merged
Changes from all commits
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
117 changes: 73 additions & 44 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) {
FT_ATOMIC_STORE_UINT8(values->valid, 0);
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 @@ -7256,8 +7262,8 @@ decref_maybe_delay(PyObject *obj, bool delay)
}
}

static int
set_or_clear_managed_dict(PyObject *obj, PyObject *new_dict, bool clear)
int
_PyObject_SetManagedDict(PyObject *obj, PyObject *new_dict)
{
assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT);
#ifndef NDEBUG
Expand Down Expand Up @@ -7292,8 +7298,7 @@ set_or_clear_managed_dict(PyObject *obj, PyObject *new_dict, bool clear)

// Decref for the dictionary we incref'd in try_set_dict_inline_only_or_other_dict
// while the object was locked
decref_maybe_delay((PyObject *)prev_dict,
!clear && prev_dict != cur_dict);
decref_maybe_delay((PyObject *)prev_dict, prev_dict != cur_dict);
if (err != 0) {
return err;
}
Expand All @@ -7303,7 +7308,7 @@ set_or_clear_managed_dict(PyObject *obj, PyObject *new_dict, bool clear)

if (prev_dict != NULL) {
// decref for the dictionary that we replaced
decref_maybe_delay((PyObject *)prev_dict, !clear);
decref_maybe_delay((PyObject *)prev_dict, true);
}

return 0;
Expand Down Expand Up @@ -7333,45 +7338,15 @@ set_or_clear_managed_dict(PyObject *obj, PyObject *new_dict, bool clear)
(PyDictObject *)Py_XNewRef(new_dict));

Py_END_CRITICAL_SECTION();
decref_maybe_delay((PyObject *)dict, !clear);
decref_maybe_delay((PyObject *)dict, true);
}
assert(_PyObject_InlineValuesConsistencyCheck(obj));
return err;
}

int
_PyObject_SetManagedDict(PyObject *obj, PyObject *new_dict)
{
return set_or_clear_managed_dict(obj, new_dict, false);
}

void
PyObject_ClearManagedDict(PyObject *obj)
{
if (set_or_clear_managed_dict(obj, NULL, true) < 0) {
/* Must be out of memory */
assert(PyErr_Occurred() == PyExc_MemoryError);
PyErr_FormatUnraisable("Exception ignored while "
"clearing an object managed dict");
/* Clear the dict */
PyDictObject *dict = _PyObject_GetManagedDict(obj);
Py_BEGIN_CRITICAL_SECTION2(dict, obj);
dict = _PyObject_ManagedDictPointer(obj)->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();
}
}

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

Expand Down Expand Up @@ -7401,6 +7376,60 @@ _PyDict_DetachFromObject(PyDictObject *mp, PyObject *obj)
return 0;
}


void
PyObject_ClearManagedDict(PyObject *obj)
{
// 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.
// No dict to clear, we're done
clear_inline_values(_PyObject_InlineValues(obj));
return;
}
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 = 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");
/* Clear the dict */
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);
clear_inline_values(_PyObject_InlineValues(obj));
Py_END_CRITICAL_SECTION();
}
}
}
Py_CLEAR(_PyObject_ManagedDictPointer(obj)->dict);
}

int
_PyDict_DetachFromObject(PyDictObject *mp, PyObject *obj)
{
ASSERT_WORLD_STOPPED_OR_OBJ_LOCKED(obj);

return detach_dict_from_object(mp, obj);
}

static inline PyObject *
ensure_managed_dict(PyObject *obj)
{
Expand Down
Loading