https://github.com/python/cpython/commit/f6842ddae6b0c744b89c33b9bb088d7c4b3eb8d3
commit: f6842ddae6b0c744b89c33b9bb088d7c4b3eb8d3
branch: 3.14
author: Donghee Na <[email protected]>
committer: corona10 <[email protected]>
date: 2026-09-01T23:36:06Z
summary:

[3.14] gh-156371: Fix missing PyRefTracer_DESTROY events for trashcan objects 
(gh-156372) (gh-156802)

(cherry picked from commit 3dfa5745f47370e86f075d356da1f4519e3077ac)

files:
A 
Misc/NEWS.d/next/Core_and_Builtins/2026-08-26-01-08-38.gh-issue-156371.USpwKG.rst
M Lib/test/test_capi/test_object.py
M Modules/_testcapimodule.c
M Objects/object.c

diff --git a/Lib/test/test_capi/test_object.py 
b/Lib/test/test_capi/test_object.py
index 0b188fc90597744..e2345635c473257 100644
--- a/Lib/test/test_capi/test_object.py
+++ b/Lib/test/test_capi/test_object.py
@@ -260,5 +260,21 @@ def func(x):
         for i in [0, 1, 2]:
             
self.assertFalse(_testcapi.pyobject_is_unique_temporary_new_object())
 
+
+class RefTracerTest(unittest.TestCase):
+    @support.requires_resource('cpu')
+    @support.skip_emscripten_stack_overflow()
+    @support.skip_wasi_stack_overflow()
+    def test_destroy_traced_for_trashcan_deferred_objects(self):
+        depth = 200_000
+        chain = None
+        for _ in range(depth):
+            chain = [chain]
+        _testcapi.start_counting_list_destroys()
+        del chain
+        destroys = _testcapi.stop_counting_list_destroys()
+        self.assertEqual(destroys, depth)
+
+
 if __name__ == "__main__":
     unittest.main()
diff --git 
a/Misc/NEWS.d/next/Core_and_Builtins/2026-08-26-01-08-38.gh-issue-156371.USpwKG.rst
 
b/Misc/NEWS.d/next/Core_and_Builtins/2026-08-26-01-08-38.gh-issue-156371.USpwKG.rst
new file mode 100644
index 000000000000000..31ebff6f247f7e3
--- /dev/null
+++ 
b/Misc/NEWS.d/next/Core_and_Builtins/2026-08-26-01-08-38.gh-issue-156371.USpwKG.rst
@@ -0,0 +1,2 @@
+Fix missing ``PyRefTracer_DESTROY`` events for trashcan deferred objects.
+Patch by Donghee Na.
diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c
index d50838e876f24ae..ee6ab7ab2c68771 100644
--- a/Modules/_testcapimodule.c
+++ b/Modules/_testcapimodule.c
@@ -39,6 +39,7 @@ static struct PyModuleDef _testcapimodule;
 // Module state
 typedef struct {
     PyObject *error; // _testcapi.error object
+    Py_ssize_t list_destroys;
 } testcapistate_t;
 
 static testcapistate_t*
@@ -2410,6 +2411,36 @@ test_reftracer(PyObject *ob, PyObject 
*Py_UNUSED(ignored))
     return NULL;
 }
 
+static int
+_listdestroytracer(PyObject *obj, PyRefTracerEvent event, void *data)
+{
+    if (event == PyRefTracer_DESTROY && PyList_CheckExact(obj)) {
+        _Py_atomic_add_ssize((Py_ssize_t *)data, 1);
+    }
+    return 0;
+}
+
+static PyObject *
+start_counting_list_destroys(PyObject *self, PyObject *Py_UNUSED(ignored))
+{
+    testcapistate_t *state = get_testcapi_state(self);
+    _Py_atomic_store_ssize(&state->list_destroys, 0);
+    if (PyRefTracer_SetTracer(_listdestroytracer, &state->list_destroys) != 0) 
{
+        return NULL;
+    }
+    Py_RETURN_NONE;
+}
+
+static PyObject *
+stop_counting_list_destroys(PyObject *self, PyObject *Py_UNUSED(ignored))
+{
+    if (PyRefTracer_SetTracer(NULL, NULL) != 0) {
+        return NULL;
+    }
+    return PyLong_FromSsize_t(
+        _Py_atomic_load_ssize(&get_testcapi_state(self)->list_destroys));
+}
+
 static PyObject *
 function_set_warning(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
 {
@@ -2614,6 +2645,8 @@ static PyMethodDef TestMethods[] = {
     {"test_buildvalue_N",        test_buildvalue_N,              METH_NOARGS},
     {"test_buildvalue_p",       test_buildvalue_p,               METH_NOARGS},
     {"test_reftracer",          test_reftracer,                  METH_NOARGS},
+    {"start_counting_list_destroys", start_counting_list_destroys, 
METH_NOARGS},
+    {"stop_counting_list_destroys", stop_counting_list_destroys, METH_NOARGS},
     {"_test_thread_state",      test_thread_state,               METH_VARARGS},
     {"gilstate_ensure_release", gilstate_ensure_release,         METH_NOARGS},
 #ifndef MS_WINDOWS
diff --git a/Objects/object.c b/Objects/object.c
index 3a104bcd932cd4f..b17a5bd70caf91d 100644
--- a/Objects/object.c
+++ b/Objects/object.c
@@ -3120,6 +3120,10 @@ _PyTrash_thread_destroy_chain(PyThreadState *tstate)
          * up distorting allocation statistics.
          */
         _PyObject_ASSERT(op, Py_REFCNT(op) == 0);
+#ifdef Py_TRACE_REFS
+        _Py_ForgetReference(op);
+#endif
+        _PyReftracerTrack(op, PyRefTracer_DESTROY);
         (*dealloc)(op);
     }
 }

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to