mirror of
https://github.com/python/cpython.git
synced 2026-06-04 08:40:58 +00:00
gh-148613: Fix race in gc_set_threshold and gc_get_threshold (#150356)
This commit is contained in:
parent
494f2e3c92
commit
41eb8ee2bb
3 changed files with 35 additions and 0 deletions
|
|
@ -94,6 +94,36 @@ def evil():
|
|||
thread.start()
|
||||
thread.join()
|
||||
|
||||
def test_set_threshold(self):
|
||||
# GH-148613: Setting the GC threshold from another thread could cause a
|
||||
# race between the `gc_should_collect` and `gc_set_threshold` functions.
|
||||
NUM_THREADS = 8
|
||||
NUM_ITERS = 100_000
|
||||
barrier = threading.Barrier(NUM_THREADS)
|
||||
|
||||
class CyclicReference:
|
||||
def __init__(self):
|
||||
self.r = self
|
||||
|
||||
def allocator():
|
||||
barrier.wait()
|
||||
for _ in range(NUM_ITERS):
|
||||
CyclicReference()
|
||||
|
||||
def setter():
|
||||
barrier.wait()
|
||||
for i in range(NUM_ITERS):
|
||||
gc.set_threshold(100 + (i % 100), 10 + (i % 10), 10 + (i % 10))
|
||||
|
||||
current_threshold = gc.get_threshold()
|
||||
try:
|
||||
threads = [Thread(target=allocator) for _ in range(NUM_THREADS - 1)]
|
||||
threads.append(Thread(target=setter))
|
||||
with threading_helper.start_threads(threads):
|
||||
pass
|
||||
finally:
|
||||
gc.set_threshold(*current_threshold)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
Fix a data race in the free-threaded build between :func:`gc.set_threshold`
|
||||
and garbage collection scheduling during object allocation.
|
||||
|
|
@ -167,6 +167,8 @@ gc_set_threshold_impl(PyObject *module, int threshold0, int group_right_1,
|
|||
gcstate->generations[2].threshold = threshold2;
|
||||
}
|
||||
#else
|
||||
PyInterpreterState *interp = _PyInterpreterState_GET();
|
||||
_PyEval_StopTheWorld(interp);
|
||||
gcstate->young.threshold = threshold0;
|
||||
if (group_right_1) {
|
||||
gcstate->old[0].threshold = threshold1;
|
||||
|
|
@ -174,6 +176,7 @@ gc_set_threshold_impl(PyObject *module, int threshold0, int group_right_1,
|
|||
if (group_right_2) {
|
||||
gcstate->old[1].threshold = threshold2;
|
||||
}
|
||||
_PyEval_StartTheWorld(interp);
|
||||
#endif
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue