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]

Reply via email to