From 4d83f6ddc413204943097699ef84a30356ba8cce Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 15 Oct 2024 19:35:28 +0200 Subject: [PATCH] [3.13] gh-124375: Avoid calling `_PyMem_ProcessDelayed` on other thread states (GH-124459) (#125540) This fixes a crash when running the PyO3 test suite on the free-threaded build. The `qsbr` field is initialized after the `PyThreadState` is added to the interpreter's linked list -- it might still be NULL. Instead, we "steal" the queue of to-be-freed memory blocks. This is always initialized (possibly empty) and protected by the stop the world pause. (cherry picked from commit 54c6fcbefd33a8d8bf8c004cf1aad3be3d37b933) Co-authored-by: Sam Gross --- ...4-10-15-15-29-41.gh-issue-124375.wNrWVa.rst | 1 + Python/gc_free_threading.c | 18 ++++++++++++------ 2 files changed, 13 insertions(+), 6 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-10-15-15-29-41.gh-issue-124375.wNrWVa.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-10-15-15-29-41.gh-issue-124375.wNrWVa.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-10-15-15-29-41.gh-issue-124375.wNrWVa.rst new file mode 100644 index 00000000000000..4bd17a6e683bc7 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2024-10-15-15-29-41.gh-issue-124375.wNrWVa.rst @@ -0,0 +1 @@ +Fix a crash in the free threading build when the GC runs concurrently with a new thread starting. diff --git a/Python/gc_free_threading.c b/Python/gc_free_threading.c index 140f1eaa88bfc2..1d8641b91da662 100644 --- a/Python/gc_free_threading.c +++ b/Python/gc_free_threading.c @@ -340,18 +340,24 @@ merge_all_queued_objects(PyInterpreterState *interp, struct collection_state *st static void process_delayed_frees(PyInterpreterState *interp) { - // In STW status, we can observe the latest write sequence by - // advancing the write sequence immediately. + // While we are in a "stop the world" pause, we can observe the latest + // write sequence by advancing the write sequence immediately. _Py_qsbr_advance(&interp->qsbr); _PyThreadStateImpl *current_tstate = (_PyThreadStateImpl *)_PyThreadState_GET(); _Py_qsbr_quiescent_state(current_tstate->qsbr); + + // Merge the queues from other threads into our own queue so that we can + // process all of the pending delayed free requests at once. HEAD_LOCK(&_PyRuntime); - PyThreadState *tstate = interp->threads.head; - while (tstate != NULL) { - _PyMem_ProcessDelayed(tstate); - tstate = (PyThreadState *)tstate->next; + for (PyThreadState *p = interp->threads.head; p != NULL; p = p->next) { + _PyThreadStateImpl *other = (_PyThreadStateImpl *)p; + if (other != current_tstate) { + llist_concat(¤t_tstate->mem_free_queue, &other->mem_free_queue); + } } HEAD_UNLOCK(&_PyRuntime); + + _PyMem_ProcessDelayed((PyThreadState *)current_tstate); } // Subtract an incoming reference from the computed "gc_refs" refcount.