diff options
author | mpage <mpage@meta.com> | 2024-04-29 16:56:51 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-04-29 16:56:51 (GMT) |
commit | 43fa76638fc75958b592096b6830c15f0afa1a73 (patch) | |
tree | c65c40432a3785354cef4633c4e8fa87f7e2029d | |
parent | 444ac0b7a64ff6b6caba9c2731bd33151ce18ad1 (diff) | |
download | cpython-43fa76638fc75958b592096b6830c15f0afa1a73.zip cpython-43fa76638fc75958b592096b6830c15f0afa1a73.tar.gz cpython-43fa76638fc75958b592096b6830c15f0afa1a73.tar.bz2 |
gh-118331: Don't raise an error if tuple allocation fails when clearing weakrefs (#118338)
It's not safe to raise an exception in `PyObject_ClearWeakRefs()` if one
is not already set, since it may be called by `_Py_Dealloc()`, which
requires that the active exception does not change.
Additionally, make sure we clear the weakrefs even when tuple allocation
fails.
-rw-r--r-- | Lib/test/test_weakref.py | 26 | ||||
-rw-r--r-- | Objects/weakrefobject.c | 4 |
2 files changed, 29 insertions, 1 deletions
diff --git a/Lib/test/test_weakref.py b/Lib/test/test_weakref.py index 499ba77..a2f5b9b 100644 --- a/Lib/test/test_weakref.py +++ b/Lib/test/test_weakref.py @@ -10,6 +10,7 @@ import copy import threading import time import random +import textwrap from test import support from test.support import script_helper, ALWAYS_EQ @@ -1009,6 +1010,31 @@ class ReferencesTestCase(TestBase): del x support.gc_collect() + @support.cpython_only + def test_no_memory_when_clearing(self): + # gh-118331: Make sure we do not raise an exception from the destructor + # when clearing weakrefs if allocating the intermediate tuple fails. + code = textwrap.dedent(""" + import _testcapi + import weakref + + class TestObj: + pass + + def callback(obj): + pass + + obj = TestObj() + # The choice of 50 is arbitrary, but must be large enough to ensure + # the allocation won't be serviced by the free list. + wrs = [weakref.ref(obj, callback) for _ in range(50)] + _testcapi.set_nomemory(0) + del obj + """).strip() + res, _ = script_helper.run_python_until_end("-c", code) + stderr = res.err.decode("ascii", "backslashreplace") + self.assertNotRegex(stderr, "_Py_Dealloc: Deallocator of type 'TestObj'") + class SubclassableWeakrefTestCase(TestBase): diff --git a/Objects/weakrefobject.c b/Objects/weakrefobject.c index 206107e..93c5fe3 100644 --- a/Objects/weakrefobject.c +++ b/Objects/weakrefobject.c @@ -1016,7 +1016,9 @@ PyObject_ClearWeakRefs(PyObject *object) PyObject *exc = PyErr_GetRaisedException(); PyObject *tuple = PyTuple_New(num_weakrefs * 2); if (tuple == NULL) { - _PyErr_ChainExceptions1(exc); + _PyWeakref_ClearWeakRefsExceptCallbacks(object); + PyErr_WriteUnraisable(NULL); + PyErr_SetRaisedException(exc); return; } |