Skip to content

Commit 12af26d

Browse files
authored
gh-150411: fix gc_generation.count race in free-threading (#150413)
1 parent 884ac3e commit 12af26d

3 files changed

Lines changed: 32 additions & 1 deletion

File tree

Lib/test/test_free_threading/test_gc.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,35 @@ def setter():
124124
finally:
125125
gc.set_threshold(*current_threshold)
126126

127+
def test_get_count(self):
128+
class CyclicReference:
129+
def __init__(self):
130+
self.ref = self
131+
132+
NUM_ALLOCATORS = 7
133+
NUM_READERS = 1
134+
NUM_THREADS = NUM_ALLOCATORS + NUM_READERS
135+
NUM_ITERS = 1000
136+
137+
barrier = threading.Barrier(NUM_THREADS)
138+
139+
def allocator():
140+
barrier.wait()
141+
for _ in range(NUM_ITERS):
142+
CyclicReference()
143+
144+
145+
def reader():
146+
barrier.wait()
147+
for _ in range(NUM_ITERS):
148+
gc.get_count()
149+
150+
threads = [Thread(target=allocator) for _ in range(NUM_ALLOCATORS)]
151+
threads.extend(Thread(target=reader) for _ in range(NUM_READERS))
152+
153+
with threading_helper.start_threads(threads):
154+
pass
155+
127156

128157
if __name__ == "__main__":
129158
unittest.main()
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix a data race in the free-threaded build when :func:`gc.get_count` reads
2+
the young generation allocation count while another thread updates it.

Modules/gcmodule.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ gc_get_count_impl(PyObject *module)
233233
gcstate->generations[2].count);
234234
#else
235235
return Py_BuildValue("(iii)",
236-
gcstate->young.count,
236+
_Py_atomic_load_int_relaxed(&gcstate->young.count),
237237
gcstate->old[0].count,
238238
gcstate->old[1].count);
239239
#endif

0 commit comments

Comments
 (0)