From e261550a26a37f2a713b8963f5f3b1398bdea9b7 Mon Sep 17 00:00:00 2001 From: Anna Date: Tue, 28 Apr 2026 15:49:16 +0200 Subject: [PATCH 1/2] Fix memory allocation deadlock in qsbr code under free-threading. Previously, a thread attempting to allocate a QSBR thread state entry would acquire a lock (`shared->mutex`). If no free entry was immediately available, this thread, while still holding the lock, would trigger a "Stop The World" event to resize the underlying array. This could lead to a deadlock if other threads needed to acquire `shared->mutex` to park themselves as part of the "Stop The World" process. See https://github.com/python/cpython/issues/148953 --- Python/qsbr.c | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/Python/qsbr.c b/Python/qsbr.c index e9d935bfb40d84..d439c2f36bfe52 100644 --- a/Python/qsbr.c +++ b/Python/qsbr.c @@ -197,12 +197,29 @@ _Py_qsbr_reserve(PyInterpreterState *interp) // Try allocating from our internal freelist struct _qsbr_thread_state *qsbr = qsbr_allocate(shared); - // If there are no free entries, we pause all threads, grow the array, - // and update the pointers in PyThreadState to entries in the new array. - if (qsbr == NULL) { + while (qsbr == NULL) { + // Unlock before stopping the world to avoid deadlocks. + // If we hold shared->mutex while waiting for the world to stop, + // we might block a thread that needs to acquire shared->mutex to park. + PyMutex_Unlock(&shared->mutex); _PyEval_StopTheWorld(interp); + PyMutex_Lock(&shared->mutex); + + // Try allocating again, as another thread might have grown the array + // or freed an entry while we were waiting. + qsbr = qsbr_allocate(shared); + if (qsbr != NULL) { + _PyEval_StartTheWorld(interp); + break; + } + + // Still NULL, we must grow it if (grow_thread_array(shared) == 0) { qsbr = qsbr_allocate(shared); + } else { + // Failed to grow array (e.g. OOM). Break to avoid infinite loop. + _PyEval_StartTheWorld(interp); + break; } _PyEval_StartTheWorld(interp); } From 15336cb3b1d431cde48f942bfc2754893ad58cad Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Tue, 28 Apr 2026 13:56:40 +0000 Subject: [PATCH 2/2] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../next/Library/2026-04-28-13-56-38.gh-issue-148953.8_88JB.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2026-04-28-13-56-38.gh-issue-148953.8_88JB.rst diff --git a/Misc/NEWS.d/next/Library/2026-04-28-13-56-38.gh-issue-148953.8_88JB.rst b/Misc/NEWS.d/next/Library/2026-04-28-13-56-38.gh-issue-148953.8_88JB.rst new file mode 100644 index 00000000000000..f9d6d3753e885b --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-04-28-13-56-38.gh-issue-148953.8_88JB.rst @@ -0,0 +1 @@ +Fixed a deadlock in `_Py_qsbr_reserve` in the free threading build.