File tree
Lib/test/test_free_threading
Misc/NEWS.d/next/Core_and_Builtins
Modules
Original file line numberDiff line numberDiff line change@@ -94,6 +94,36 @@ def evil():
9494thread.start()
9595thread.join()
969697+deftest_set_threshold(self):
98+# GH-148613: Setting the GC threshold from another thread could cause a
99+# race between the `gc_should_collect` and `gc_set_threshold` functions.
100+NUM_THREADS=8
101+NUM_ITERS=100_000
102+barrier=threading.Barrier(NUM_THREADS)
103+104+classCyclicReference:
105+def__init__(self):
106+self.r=self
107+108+defallocator():
109+barrier.wait()
110+for_inrange(NUM_ITERS):
111+CyclicReference()
112+113+defsetter():
114+barrier.wait()
115+foriinrange(NUM_ITERS):
116+gc.set_threshold(100+ (i%100), 10+ (i%10), 10+ (i%10))
117+118+current_threshold=gc.get_threshold()
119+try:
120+threads= [Thread(target=allocator) for_inrange(NUM_THREADS-1)]
121+threads.append(Thread(target=setter))
122+withthreading_helper.start_threads(threads):
123+pass
124+finally:
125+gc.set_threshold(*current_threshold)
126+9712798128if__name__=="__main__":
99129unittest.main()
Original file line numberDiff line numberDiff line change@@ -0,0 +1,2 @@
1+Fix a data race in the free-threaded build between :func:`gc.set_threshold`
2+and garbage collection scheduling during object allocation.
Original file line numberDiff line numberDiff line change@@ -167,13 +167,16 @@ gc_set_threshold_impl(PyObject *module, int threshold0, int group_right_1,
167167gcstate->generations[2].threshold=threshold2;
168168 }
169169#else
170+PyInterpreterState*interp=_PyInterpreterState_GET();
171+_PyEval_StopTheWorld(interp);
170172gcstate->young.threshold=threshold0;
171173if (group_right_1) {
172174gcstate->old[0].threshold=threshold1;
173175 }
174176if (group_right_2) {
175177gcstate->old[1].threshold=threshold2;
176178 }
179+_PyEval_StartTheWorld(interp);
177180#endif
178181Py_RETURN_NONE;
179182}