mirror of https://github.com/python/cpython
Issue #25395: Fixed crash when highly nested OrderedDict structures were
garbage collected.
This commit is contained in:
parent
964ec8b2f3
commit
14eefe353e
|
@ -2025,6 +2025,30 @@ class OrderedDictTests:
|
||||||
items = [('a', 1), ('c', 3), ('b', 2)]
|
items = [('a', 1), ('c', 3), ('b', 2)]
|
||||||
self.assertEqual(list(MyOD(items).items()), items)
|
self.assertEqual(list(MyOD(items).items()), items)
|
||||||
|
|
||||||
|
def test_highly_nested(self):
|
||||||
|
# Issue 25395: crashes during garbage collection
|
||||||
|
OrderedDict = self.module.OrderedDict
|
||||||
|
obj = None
|
||||||
|
for _ in range(1000):
|
||||||
|
obj = OrderedDict([(None, obj)])
|
||||||
|
del obj
|
||||||
|
support.gc_collect()
|
||||||
|
|
||||||
|
def test_highly_nested_subclass(self):
|
||||||
|
# Issue 25395: crashes during garbage collection
|
||||||
|
OrderedDict = self.module.OrderedDict
|
||||||
|
deleted = []
|
||||||
|
class MyOD(OrderedDict):
|
||||||
|
def __del__(self):
|
||||||
|
deleted.append(self.i)
|
||||||
|
obj = None
|
||||||
|
for i in range(100):
|
||||||
|
obj = MyOD([(None, obj)])
|
||||||
|
obj.i = i
|
||||||
|
del obj
|
||||||
|
support.gc_collect()
|
||||||
|
self.assertEqual(deleted, list(reversed(range(100))))
|
||||||
|
|
||||||
|
|
||||||
class PurePythonOrderedDictTests(OrderedDictTests, unittest.TestCase):
|
class PurePythonOrderedDictTests(OrderedDictTests, unittest.TestCase):
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,9 @@ Release date: TBA
|
||||||
Core and Builtins
|
Core and Builtins
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
- Issue #25395: Fixed crash when highly nested OrderedDict structures were
|
||||||
|
garbage collected.
|
||||||
|
|
||||||
- Issue #25274: sys.setrecursionlimit() now raises a RecursionError if the new
|
- Issue #25274: sys.setrecursionlimit() now raises a RecursionError if the new
|
||||||
recursion limit is too low depending at the current recursion depth. Modify
|
recursion limit is too low depending at the current recursion depth. Modify
|
||||||
also the "lower-water mark" formula to make it monotonic. This mark is used
|
also the "lower-water mark" formula to make it monotonic. This mark is used
|
||||||
|
|
|
@ -1431,17 +1431,28 @@ static PyMemberDef odict_members[] = {
|
||||||
static void
|
static void
|
||||||
odict_dealloc(PyODictObject *self)
|
odict_dealloc(PyODictObject *self)
|
||||||
{
|
{
|
||||||
|
PyThreadState *tstate = PyThreadState_GET();
|
||||||
|
|
||||||
PyObject_GC_UnTrack(self);
|
PyObject_GC_UnTrack(self);
|
||||||
Py_TRASHCAN_SAFE_BEGIN(self);
|
Py_TRASHCAN_SAFE_BEGIN(self)
|
||||||
|
|
||||||
Py_XDECREF(self->od_inst_dict);
|
Py_XDECREF(self->od_inst_dict);
|
||||||
if (self->od_weakreflist != NULL)
|
if (self->od_weakreflist != NULL)
|
||||||
PyObject_ClearWeakRefs((PyObject *)self);
|
PyObject_ClearWeakRefs((PyObject *)self);
|
||||||
|
|
||||||
_odict_clear_nodes(self);
|
_odict_clear_nodes(self);
|
||||||
Py_TRASHCAN_SAFE_END(self);
|
|
||||||
|
|
||||||
/* must be last */
|
/* Call the base tp_dealloc(). Since it too uses the trashcan mechanism,
|
||||||
|
* temporarily decrement trash_delete_nesting to prevent triggering it
|
||||||
|
* and putting the partially deallocated object on the trashcan's
|
||||||
|
* to-be-deleted-later list.
|
||||||
|
*/
|
||||||
|
--tstate->trash_delete_nesting;
|
||||||
|
assert(_tstate->trash_delete_nesting < PyTrash_UNWIND_LEVEL);
|
||||||
PyDict_Type.tp_dealloc((PyObject *)self);
|
PyDict_Type.tp_dealloc((PyObject *)self);
|
||||||
|
++tstate->trash_delete_nesting;
|
||||||
|
|
||||||
|
Py_TRASHCAN_SAFE_END(self)
|
||||||
};
|
};
|
||||||
|
|
||||||
/* tp_repr */
|
/* tp_repr */
|
||||||
|
|
Loading…
Reference in New Issue