https://github.com/python/cpython/commit/56cc48239db56d28032e463832f936b46dad4289
commit: 56cc48239db56d28032e463832f936b46dad4289
branch: 3.14
author: Miss Islington (bot) <[email protected]>
committer: DinoV <[email protected]>
date: 2026-08-28T09:51:31-07:00
summary:

[3.14] gh-156395: Report frozen objects on free-threaded GC build (GH-156396) 
(#156421)

gh-156395: Report frozen objects on free-threaded GC build (GH-156396)

files:
A Misc/NEWS.d/next/C_API/2026-08-25-12-00-00.gh-issue-131740.Ft7Rz2.rst
M Modules/_testcapi/gc.c
M Python/gc_free_threading.c

diff --git 
a/Misc/NEWS.d/next/C_API/2026-08-25-12-00-00.gh-issue-131740.Ft7Rz2.rst 
b/Misc/NEWS.d/next/C_API/2026-08-25-12-00-00.gh-issue-131740.Ft7Rz2.rst
new file mode 100644
index 000000000000000..83701b66ca85f85
--- /dev/null
+++ b/Misc/NEWS.d/next/C_API/2026-08-25-12-00-00.gh-issue-131740.Ft7Rz2.rst
@@ -0,0 +1,3 @@
+On the free-threaded build, :c:func:`PyUnstable_GC_VisitObjects` now also
+visits frozen objects (objects moved to the permanent generation by
+:func:`gc.freeze`), matching the behavior of the default build.
diff --git a/Modules/_testcapi/gc.c b/Modules/_testcapi/gc.c
index 863cb52980f9425..dac7066565f7ffc 100644
--- a/Modules/_testcapi/gc.c
+++ b/Modules/_testcapi/gc.c
@@ -195,6 +195,57 @@ test_gc_visit_objects_basic(PyObject *Py_UNUSED(self),
     Py_RETURN_NONE;
 }
 
+static int
+gc_call_no_args(const char *method)
+{
+    PyObject *gc = PyImport_ImportModule("gc");
+    if (gc == NULL) {
+        return -1;
+    }
+    PyObject *res = PyObject_CallMethod(gc, method, NULL);
+    Py_DECREF(gc);
+    if (res == NULL) {
+        return -1;
+    }
+    Py_DECREF(res);
+    return 0;
+}
+
+// gh-131740: frozen objects must be visited too.
+static PyObject *
+test_gc_visit_objects_frozen(PyObject *Py_UNUSED(self),
+                             PyObject *Py_UNUSED(ignored))
+{
+    PyObject *obj;
+    struct gc_visit_state_basic state;
+
+    obj = PyList_New(0);
+    if (obj == NULL) {
+        return NULL;
+    }
+    if (gc_call_no_args("freeze") < 0) {
+        Py_DECREF(obj);
+        return NULL;
+    }
+    state.target = obj;
+    state.found = 0;
+
+    PyUnstable_GC_VisitObjects(gc_visit_callback_basic, &state);
+
+    int err = gc_call_no_args("unfreeze");
+    Py_DECREF(obj);
+    if (err < 0) {
+        return NULL;
+    }
+    if (!state.found) {
+        PyErr_SetString(
+             PyExc_AssertionError,
+             "test_gc_visit_objects_frozen: Didn't find frozen list");
+         return NULL;
+    }
+    Py_RETURN_NONE;
+}
+
 static int
 gc_visit_callback_exit_early(PyObject *obj, void *arg)
  {
@@ -316,6 +367,7 @@ static PyType_Spec ObjExtraData_TypeSpec = {
 static PyMethodDef test_methods[] = {
     {"test_gc_control", test_gc_control, METH_NOARGS},
     {"test_gc_visit_objects_basic", test_gc_visit_objects_basic, METH_NOARGS, 
NULL},
+    {"test_gc_visit_objects_frozen", test_gc_visit_objects_frozen, 
METH_NOARGS, NULL},
     {"test_gc_visit_objects_exit_early", test_gc_visit_objects_exit_early, 
METH_NOARGS, NULL},
     {"without_gc", without_gc, METH_O, NULL},
     {"with_tp_del", with_tp_del, METH_VARARGS, NULL},
diff --git a/Python/gc_free_threading.c b/Python/gc_free_threading.c
index d1b8d282415337a..285f24806e8fd4f 100644
--- a/Python/gc_free_threading.c
+++ b/Python/gc_free_threading.c
@@ -2953,7 +2953,7 @@ static bool
 custom_visitor_wrapper(const mi_heap_t *heap, const mi_heap_area_t *area,
                        void *block, size_t block_size, void *args)
 {
-    PyObject *op = op_from_block(block, args, false);
+    PyObject *op = op_from_block(block, args, true);
     if (op == NULL) {
         return true;
     }

_______________________________________________
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