diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-12-06-01-25-04.gh-issue-141658.g039M-.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-12-06-01-25-04.gh-issue-141658.g039M-.rst new file mode 100644 index 00000000000000..d8771c817d1106 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-12-06-01-25-04.gh-issue-141658.g039M-.rst @@ -0,0 +1,2 @@ +Fix a deadlock where JIT executor drain could run under the rutime lock when +invalidations overflow the pending list. diff --git a/Python/optimizer.c b/Python/optimizer.c index 9db894f0bf054a..647c634bdac2a5 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -293,6 +293,17 @@ _Py_ClearExecutorDeletionList(PyInterpreterState *interp) interp->executor_deletion_list_remaining_capacity = EXECUTOR_DELETE_LIST_MAX; } +static inline int +can_clear_executor_deletion_list(PyInterpreterState *interp) +{ +#ifdef Py_GIL_DISABLED + if (_PyRuntime.stoptheworld.world_stopped || interp->stoptheworld.world_stopped) { + return 0; + } +#endif + return 1; +} + static void add_to_pending_deletion_list(_PyExecutorObject *self) { @@ -302,7 +313,7 @@ add_to_pending_deletion_list(_PyExecutorObject *self) if (interp->executor_deletion_list_remaining_capacity > 0) { interp->executor_deletion_list_remaining_capacity--; } - else { + else if (can_clear_executor_deletion_list(interp)) { _Py_ClearExecutorDeletionList(interp); } } diff --git a/Python/pystate.c b/Python/pystate.c index c12a1418e74309..c0e02aeec51e12 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -2358,6 +2358,18 @@ start_the_world(struct _stoptheworld_state *stw) _PyRuntimeState *runtime = &_PyRuntime; assert(PyMutex_IsLocked(&stw->mutex)); +#ifdef _Py_TIER2 + if (stw->is_global) { + _Py_FOR_EACH_STW_INTERP(stw, interp) { + _Py_ClearExecutorDeletionList(interp); + } + } + else { + PyInterpreterState *interp = _Py_CONTAINER_OF(stw, PyInterpreterState, stoptheworld); + _Py_ClearExecutorDeletionList(interp); + } +#endif + HEAD_LOCK(runtime); stw->requested = 0; stw->world_stopped = 0;