summaryrefslogtreecommitdiffstats
path: root/Lib/test
diff options
context:
space:
mode:
authorJeroen Demeyer <J.Demeyer@UGent.be>2019-05-10 17:21:11 (GMT)
committerAntoine Pitrou <antoine@python.org>2019-05-10 17:21:10 (GMT)
commit351c67416ba4451eb3928fa0b2e933c2f25df1a3 (patch)
tree5054fe93291fa93533ddd97622f329e96a31847e /Lib/test
parenta2fedd8c910cb5f5b9bd568d6fd44d63f8f5cfa5 (diff)
downloadcpython-351c67416ba4451eb3928fa0b2e933c2f25df1a3.zip
cpython-351c67416ba4451eb3928fa0b2e933c2f25df1a3.tar.gz
cpython-351c67416ba4451eb3928fa0b2e933c2f25df1a3.tar.bz2
bpo-35983: skip trashcan for subclasses (GH-11841)
Add new trashcan macros to deal with a double deallocation that could occur when the `tp_dealloc` of a subclass calls the `tp_dealloc` of a base class and that base class uses the trashcan mechanism. Patch by Jeroen Demeyer.
Diffstat (limited to 'Lib/test')
-rw-r--r--Lib/test/test_capi.py43
-rw-r--r--Lib/test/test_ordered_dict.py8
2 files changed, 49 insertions, 2 deletions
diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py
index 31dab6a..8bcbd82 100644
--- a/Lib/test/test_capi.py
+++ b/Lib/test/test_capi.py
@@ -333,6 +333,49 @@ class CAPITest(unittest.TestCase):
br'_Py_NegativeRefcount: Assertion failed: '
br'object has negative ref count')
+ def test_trashcan_subclass(self):
+ # bpo-35983: Check that the trashcan mechanism for "list" is NOT
+ # activated when its tp_dealloc is being called by a subclass
+ from _testcapi import MyList
+ L = None
+ for i in range(1000):
+ L = MyList((L,))
+
+ def test_trashcan_python_class1(self):
+ self.do_test_trashcan_python_class(list)
+
+ def test_trashcan_python_class2(self):
+ from _testcapi import MyList
+ self.do_test_trashcan_python_class(MyList)
+
+ def do_test_trashcan_python_class(self, base):
+ # Check that the trashcan mechanism works properly for a Python
+ # subclass of a class using the trashcan (this specific test assumes
+ # that the base class "base" behaves like list)
+ class PyList(base):
+ # Count the number of PyList instances to verify that there is
+ # no memory leak
+ num = 0
+ def __init__(self, *args):
+ __class__.num += 1
+ super().__init__(*args)
+ def __del__(self):
+ __class__.num -= 1
+
+ for parity in (0, 1):
+ L = None
+ # We need in the order of 2**20 iterations here such that a
+ # typical 8MB stack would overflow without the trashcan.
+ for i in range(2**20):
+ L = PyList((L,))
+ L.attr = i
+ if parity:
+ # Add one additional nesting layer
+ L = (L,)
+ self.assertGreater(PyList.num, 0)
+ del L
+ self.assertEqual(PyList.num, 0)
+
class TestPendingCalls(unittest.TestCase):
diff --git a/Lib/test/test_ordered_dict.py b/Lib/test/test_ordered_dict.py
index b1d7f86..148a9bd 100644
--- a/Lib/test/test_ordered_dict.py
+++ b/Lib/test/test_ordered_dict.py
@@ -459,7 +459,9 @@ class OrderedDictTests:
self.assertEqual(list(MyOD(items).items()), items)
def test_highly_nested(self):
- # Issue 25395: crashes during garbage collection
+ # Issues 25395 and 35983: test that the trashcan mechanism works
+ # correctly for OrderedDict: deleting a highly nested OrderDict
+ # should not crash Python.
OrderedDict = self.OrderedDict
obj = None
for _ in range(1000):
@@ -468,7 +470,9 @@ class OrderedDictTests:
support.gc_collect()
def test_highly_nested_subclass(self):
- # Issue 25395: crashes during garbage collection
+ # Issues 25395 and 35983: test that the trashcan mechanism works
+ # correctly for OrderedDict: deleting a highly nested OrderDict
+ # should not crash Python.
OrderedDict = self.OrderedDict
deleted = []
class MyOD(OrderedDict):