Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Include/internal/pycore_interp_structs.h
Original file line numberDiff line numberDiff line change
Expand Up@@ -965,6 +965,7 @@ struct _is{
# endif
#endif

Py_ssize_t owners;
/* the initial PyInterpreterState.threads.head */
_PyThreadStateImpl _initial_thread;
// _initial_thread should be the last field of PyInterpreterState.
Expand Down
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
Fix a TSAN-reported data race and potential use-after-free when shutting down a subinterpreter with running daemon threads in free-threaded (NOGIL) builds.
18 changes: 15 additions & 3 deletions Python/pystate.c
Original file line numberDiff line numberDiff line change
Expand Up@@ -488,6 +488,15 @@ free_interpreter(PyInterpreterState *interp)
}
}

static inline void
release_interp_owner(PyInterpreterState *interp)
{
Py_ssize_t prev = _Py_atomic_add_ssize(&interp->owners, -1);
if (prev == 1){
free_interpreter(interp);
}
}

#ifndef NDEBUG
static inline int check_interpreter_whence(long);
#endif
Expand DownExpand Up@@ -537,6 +546,7 @@ init_interpreter(PyInterpreterState *interp,
interp->id = id;

interp->id_refcount = 0;
interp->owners = 1;

assert(runtime->interpreters.head == interp);
assert(next != NULL || (interp == runtime->interpreters.main));
Expand DownExpand Up@@ -960,10 +970,8 @@ PyInterpreterState_Delete(PyInterpreterState *interp)
HEAD_UNLOCK(runtime);

_Py_qsbr_fini(interp);

_PyObject_FiniState(interp);

free_interpreter(interp);
release_interp_owner(interp);
}


Expand DownExpand Up@@ -1427,6 +1435,8 @@ free_threadstate(_PyThreadStateImpl *tstate)
else{
PyMem_RawFree(tstate);
}

release_interp_owner(interp);
}

static void
Expand DownExpand Up@@ -1553,6 +1563,8 @@ new_threadstate(PyInterpreterState *interp, int whence)
uint64_t id = interp->threads.next_unique_id;
init_threadstate(tstate, interp, id, whence);

_Py_atomic_add_ssize(&interp->owners, 1);

// Add the new thread state to the interpreter.
PyThreadState *old_head = interp->threads.head;
add_threadstate(interp, (PyThreadState *)tstate, old_head);
Expand Down
Loading