Revert "gh-128639: Don't assume one thread in subinterpreter finalization (gh-128640)" (gh-134256)

This reverts commit 9859791f9e.

The original change broke the iOS and android buildbots, where the tests are run single-process.
This commit is contained in:
Peter Bierma 2025-05-19 14:22:05 -04:00 committed by GitHub
parent 871d269875
commit 27bd08273c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 38 additions and 99 deletions

View file

@ -1992,7 +1992,6 @@ resolve_final_tstate(_PyRuntimeState *runtime)
}
else {
/* Fall back to the current tstate. It's better than nothing. */
// XXX No it's not
main_tstate = tstate;
}
}
@ -2038,16 +2037,6 @@ _Py_Finalize(_PyRuntimeState *runtime)
_PyAtExit_Call(tstate->interp);
/* Clean up any lingering subinterpreters.
Two preconditions need to be met here:
- This has to happen before _PyRuntimeState_SetFinalizing is
called, or else threads might get prematurely blocked.
- The world must not be stopped, as finalizers can run.
*/
finalize_subinterpreters();
assert(_PyThreadState_GET() == tstate);
/* Copy the core config, PyInterpreterState_Delete() free
@ -2135,6 +2124,9 @@ _Py_Finalize(_PyRuntimeState *runtime)
_PyImport_FiniExternal(tstate->interp);
finalize_modules(tstate);
/* Clean up any lingering subinterpreters. */
finalize_subinterpreters();
/* Print debug stats if any */
_PyEval_Fini();
@ -2418,8 +2410,9 @@ Py_NewInterpreter(void)
return tstate;
}
/* Delete an interpreter. This requires that the given thread state
is current, and that the thread has no remaining frames.
/* Delete an interpreter and its last thread. This requires that the
given thread state is current, that the thread has no remaining
frames, and that it is its interpreter's only remaining thread.
It is a fatal error to violate these constraints.
(Py_FinalizeEx() doesn't have these constraints -- it zaps
@ -2449,15 +2442,14 @@ Py_EndInterpreter(PyThreadState *tstate)
_Py_FinishPendingCalls(tstate);
_PyAtExit_Call(tstate->interp);
_PyRuntimeState *runtime = interp->runtime;
_PyEval_StopTheWorldAll(runtime);
PyThreadState *list = _PyThreadState_RemoveExcept(tstate);
if (tstate != interp->threads.head || tstate->next != NULL) {
Py_FatalError("not the last thread");
}
/* Remaining daemon threads will automatically exit
when they attempt to take the GIL (ex: PyEval_RestoreThread()). */
_PyInterpreterState_SetFinalizing(interp, tstate);
_PyEval_StartTheWorldAll(runtime);
_PyThreadState_DeleteList(list, /*is_after_fork=*/0);
// XXX Call something like _PyImport_Disable() here?
@ -2488,8 +2480,6 @@ finalize_subinterpreters(void)
PyInterpreterState *main_interp = _PyInterpreterState_Main();
assert(final_tstate->interp == main_interp);
_PyRuntimeState *runtime = main_interp->runtime;
assert(!runtime->stoptheworld.world_stopped);
assert(_PyRuntimeState_GetFinalizing(runtime) == NULL);
struct pyinterpreters *interpreters = &runtime->interpreters;
/* Get the first interpreter in the list. */
@ -2518,17 +2508,27 @@ finalize_subinterpreters(void)
/* Clean up all remaining subinterpreters. */
while (interp != NULL) {
/* Make a tstate for finalization. */
PyThreadState *tstate = _PyThreadState_NewBound(interp, _PyThreadState_WHENCE_FINI);
if (tstate == NULL) {
// XXX Some graceful way to always get a thread state?
Py_FatalError("thread state allocation failed");
}
assert(!_PyInterpreterState_IsRunningMain(interp));
/* Enter the subinterpreter. */
_PyThreadState_Attach(tstate);
/* Find the tstate to use for fini. We assume the interpreter
will have at most one tstate at this point. */
PyThreadState *tstate = interp->threads.head;
if (tstate != NULL) {
/* Ideally we would be able to use tstate as-is, and rely
on it being in a ready state: no exception set, not
running anything (tstate->current_frame), matching the
current thread ID (tstate->thread_id). To play it safe,
we always delete it and use a fresh tstate instead. */
assert(tstate != final_tstate);
_PyThreadState_Attach(tstate);
PyThreadState_Clear(tstate);
_PyThreadState_Detach(tstate);
PyThreadState_Delete(tstate);
}
tstate = _PyThreadState_NewBound(interp, _PyThreadState_WHENCE_FINI);
/* Destroy the subinterpreter. */
_PyThreadState_Attach(tstate);
Py_EndInterpreter(tstate);
assert(_PyThreadState_GET() == NULL);