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.
This commit is contained in:
mpage 2024-04-29 09:56:51 -07:00 committed by GitHub
parent 444ac0b7a6
commit 43fa76638f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 29 additions and 1 deletions

View File

@ -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):

View File

@ -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;
}