https://github.com/python/cpython/commit/b3683c0ffdbed7e055b4b3196775eabae3d6f671
commit: b3683c0ffdbed7e055b4b3196775eabae3d6f671
branch: main
author: Vankudothu Adhi Naik <[email protected]>
committer: kumaraditya303 <[email protected]>
date: 2026-10-26T20:15:13+05:30
summary:
gh-160682: Track running function objects in free-threaded GC (#160707)
files:
A
Misc/NEWS.d/next/Core_and_Builtins/2026-10-26-12-00-00.gh-issue-160682.Q7mK2x.rst
M Lib/test/test_free_threading/test_gc.py
M Python/gc_free_threading.c
diff --git a/Lib/test/test_free_threading/test_gc.py
b/Lib/test/test_free_threading/test_gc.py
index 30282a111345f86..b287b51826999c7 100644
--- a/Lib/test/test_free_threading/test_gc.py
+++ b/Lib/test/test_free_threading/test_gc.py
@@ -97,6 +97,19 @@ def evil():
thread.start()
thread.join()
+ def test_running_method_not_collected(self):
+ class C:
+ def m(self):
+ wr = weakref.ref(C.__dict__["m"])
+ del C.m
+ gc.collect()
+ return wr() is not None
+
+ def run(self):
+ return self.m()
+
+ self.assertTrue(C().run())
+
def test_merge_brc_queue_of_detached_thread(self):
# GH-157838: objects queued for merging by a thread that is detached
# (blocked in a lock acquire, sleep, etc.) are merged and freed on its
diff --git
a/Misc/NEWS.d/next/Core_and_Builtins/2026-10-26-12-00-00.gh-issue-160682.Q7mK2x.rst
b/Misc/NEWS.d/next/Core_and_Builtins/2026-10-26-12-00-00.gh-issue-160682.Q7mK2x.rst
new file mode 100644
index 000000000000000..5c783665e789659
--- /dev/null
+++
b/Misc/NEWS.d/next/Core_and_Builtins/2026-10-26-12-00-00.gh-issue-160682.Q7mK2x.rst
@@ -0,0 +1,2 @@
+Fix a bug in the :term:`free-threaded build` where garbage collection could
+prematurely collect the function object of a running method.
diff --git a/Python/gc_free_threading.c b/Python/gc_free_threading.c
index 6db010468456eaa..6eaeeea8848cdbf 100644
--- a/Python/gc_free_threading.c
+++ b/Python/gc_free_threading.c
@@ -470,6 +470,7 @@ gc_visit_thread_stacks(PyInterpreterState *interp, struct
collection_state *stat
continue;
}
+ gc_visit_stackref(f->f_funcobj);
gc_visit_stackref(f->f_executable);
while (top != f->localsplus) {
--top;
@@ -866,6 +867,10 @@ gc_visit_thread_stacks_mark_alive(PyInterpreterState
*interp, gc_mark_args_t *ar
}
_PyStackRef *top = f->stackpointer;
+ if (gc_visit_stackref_mark_alive(args, f->f_funcobj) < 0) {
+ err = -1;
+ goto exit;
+ }
if (gc_visit_stackref_mark_alive(args, f->f_executable) < 0) {
err = -1;
goto exit;
_______________________________________________
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]