]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.13] gh-124375: Avoid calling `_PyMem_ProcessDelayed` on other thread states (GH...
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Tue, 15 Oct 2024 17:35:28 +0000 (19:35 +0200)
committerGitHub <noreply@github.com>
Tue, 15 Oct 2024 17:35:28 +0000 (13:35 -0400)
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 <colesbury@gmail.com>
Misc/NEWS.d/next/Core_and_Builtins/2024-10-15-15-29-41.gh-issue-124375.wNrWVa.rst [new file with mode: 0644]
Python/gc_free_threading.c

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 (file)
index 0000000..4bd17a6
--- /dev/null
@@ -0,0 +1 @@
+Fix a crash in the free threading build when the GC runs concurrently with a new thread starting.
index 140f1eaa88bfc2bedbd317078afed5ffd7d395e2..1d8641b91da6623a63f073b59a2791286fba3cd3 100644 (file)
@@ -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(&current_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.