Skip to content

Commit b3683c0

Browse files
gh-160682: Track running function objects in free-threaded GC (#160707)
1 parent c5d0978 commit b3683c0

3 files changed

Lines changed: 20 additions & 0 deletions

File tree

‎Lib/test/test_free_threading/test_gc.py‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,19 @@ def evil():
9797
thread.start()
9898
thread.join()
9999

100+
def test_running_method_not_collected(self):
101+
class C:
102+
def m(self):
103+
wr = weakref.ref(C.__dict__["m"])
104+
del C.m
105+
gc.collect()
106+
return wr() is not None
107+
108+
def run(self):
109+
return self.m()
110+
111+
self.assertTrue(C().run())
112+
100113
def test_merge_brc_queue_of_detached_thread(self):
101114
# GH-157838: objects queued for merging by a thread that is detached
102115
# (blocked in a lock acquire, sleep, etc.) are merged and freed on its
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix a bug in the :term:`free-threaded build` where garbage collection could
2+
prematurely collect the function object of a running method.

‎Python/gc_free_threading.c‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,7 @@ gc_visit_thread_stacks(PyInterpreterState *interp, struct collection_state *stat
470470
continue;
471471
}
472472

473+
gc_visit_stackref(f->f_funcobj);
473474
gc_visit_stackref(f->f_executable);
474475
while (top != f->localsplus) {
475476
--top;
@@ -866,6 +867,10 @@ gc_visit_thread_stacks_mark_alive(PyInterpreterState *interp, gc_mark_args_t *ar
866867
}
867868

868869
_PyStackRef *top = f->stackpointer;
870+
if (gc_visit_stackref_mark_alive(args, f->f_funcobj) < 0) {
871+
err = -1;
872+
goto exit;
873+
}
869874
if (gc_visit_stackref_mark_alive(args, f->f_executable) < 0) {
870875
err = -1;
871876
goto exit;

0 commit comments

Comments
 (0)