https://github.com/python/cpython/commit/9036982ed73d17848d45b60b7550f097371214e4
commit: 9036982ed73d17848d45b60b7550f097371214e4
branch: main
author: Dino Viehland <[email protected]>
committer: DinoV <[email protected]>
date: 2026-08-26T07:29:09-07:00
summary:
gh-156395: Report frozen objects on free-threaded GC build (#156396)
Report frozen objects on free-threaded GC build
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 fbd13d1e4d87f25..f865ac2f7db0853 100644
--- a/Python/gc_free_threading.c
+++ b/Python/gc_free_threading.c
@@ -2860,7 +2860,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]