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)
{
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
}
}
-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
// 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;
}
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;
(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));
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)
{