mirror of
https://github.com/python/cpython.git
synced 2025-08-04 08:59:19 +00:00
gh-130313: Avoid locking when clearing objects (#130126)
Avoid locking when clearing objects in the free-threaded build
This commit is contained in:
parent
69426fcee7
commit
6c450f44c2
1 changed files with 73 additions and 44 deletions
|
@ -7163,6 +7163,17 @@ PyObject_VisitManagedDict(PyObject *obj, visitproc visit, void *arg)
|
||||||
return 0;
|
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
|
static void
|
||||||
set_dict_inline_values(PyObject *obj, PyDictObject *new_dict)
|
set_dict_inline_values(PyObject *obj, PyDictObject *new_dict)
|
||||||
{
|
{
|
||||||
|
@ -7173,12 +7184,7 @@ set_dict_inline_values(PyObject *obj, PyDictObject *new_dict)
|
||||||
Py_XINCREF(new_dict);
|
Py_XINCREF(new_dict);
|
||||||
FT_ATOMIC_STORE_PTR(_PyObject_ManagedDictPointer(obj)->dict, new_dict);
|
FT_ATOMIC_STORE_PTR(_PyObject_ManagedDictPointer(obj)->dict, new_dict);
|
||||||
|
|
||||||
if (values->valid) {
|
clear_inline_values(values);
|
||||||
FT_ATOMIC_STORE_UINT8(values->valid, 0);
|
|
||||||
for (Py_ssize_t i = 0; i < values->capacity; i++) {
|
|
||||||
Py_CLEAR(values->values[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef Py_GIL_DISABLED
|
#ifdef Py_GIL_DISABLED
|
||||||
|
@ -7256,8 +7262,8 @@ decref_maybe_delay(PyObject *obj, bool delay)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
int
|
||||||
set_or_clear_managed_dict(PyObject *obj, PyObject *new_dict, bool clear)
|
_PyObject_SetManagedDict(PyObject *obj, PyObject *new_dict)
|
||||||
{
|
{
|
||||||
assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT);
|
assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT);
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
|
@ -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
|
// Decref for the dictionary we incref'd in try_set_dict_inline_only_or_other_dict
|
||||||
// while the object was locked
|
// while the object was locked
|
||||||
decref_maybe_delay((PyObject *)prev_dict,
|
decref_maybe_delay((PyObject *)prev_dict, prev_dict != cur_dict);
|
||||||
!clear && prev_dict != cur_dict);
|
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
@ -7303,7 +7308,7 @@ set_or_clear_managed_dict(PyObject *obj, PyObject *new_dict, bool clear)
|
||||||
|
|
||||||
if (prev_dict != NULL) {
|
if (prev_dict != NULL) {
|
||||||
// decref for the dictionary that we replaced
|
// decref for the dictionary that we replaced
|
||||||
decref_maybe_delay((PyObject *)prev_dict, !clear);
|
decref_maybe_delay((PyObject *)prev_dict, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -7333,45 +7338,15 @@ set_or_clear_managed_dict(PyObject *obj, PyObject *new_dict, bool clear)
|
||||||
(PyDictObject *)Py_XNewRef(new_dict));
|
(PyDictObject *)Py_XNewRef(new_dict));
|
||||||
|
|
||||||
Py_END_CRITICAL_SECTION();
|
Py_END_CRITICAL_SECTION();
|
||||||
decref_maybe_delay((PyObject *)dict, !clear);
|
decref_maybe_delay((PyObject *)dict, true);
|
||||||
}
|
}
|
||||||
assert(_PyObject_InlineValuesConsistencyCheck(obj));
|
assert(_PyObject_InlineValuesConsistencyCheck(obj));
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
static int
|
||||||
_PyObject_SetManagedDict(PyObject *obj, PyObject *new_dict)
|
detach_dict_from_object(PyDictObject *mp, PyObject *obj)
|
||||||
{
|
{
|
||||||
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)
|
|
||||||
{
|
|
||||||
ASSERT_WORLD_STOPPED_OR_OBJ_LOCKED(obj);
|
|
||||||
assert(_PyObject_ManagedDictPointer(obj)->dict == mp);
|
assert(_PyObject_ManagedDictPointer(obj)->dict == mp);
|
||||||
assert(_PyObject_InlineValuesConsistencyCheck(obj));
|
assert(_PyObject_InlineValuesConsistencyCheck(obj));
|
||||||
|
|
||||||
|
@ -7401,6 +7376,60 @@ _PyDict_DetachFromObject(PyDictObject *mp, PyObject *obj)
|
||||||
return 0;
|
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 *
|
static inline PyObject *
|
||||||
ensure_managed_dict(PyObject *obj)
|
ensure_managed_dict(PyObject *obj)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue