Skip to content

Commit

Permalink
[3.13] gh-124375: Avoid calling _PyMem_ProcessDelayed on other thre…
Browse files Browse the repository at this point in the history
…ad 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 54c6fcb)

Co-authored-by: Sam Gross <[email protected]>
  • Loading branch information
miss-islington and colesbury authored Oct 15, 2024
1 parent 6c79bae commit 4d83f6d
Show file tree
Hide file tree
Showing 2 changed files with 13 additions and 6 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix a crash in the free threading build when the GC runs concurrently with a new thread starting.
18 changes: 12 additions & 6 deletions Python/gc_free_threading.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down

0 comments on commit 4d83f6d

Please sign in to comment.