aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/Python/pylifecycle.c
diff options
context:
space:
mode:
authorPeter Bierma <zintensitydev@gmail.com>2025-05-19 14:22:05 -0400
committerGitHub <noreply@github.com>2025-05-19 12:22:05 -0600
commit27bd08273ce822a4dbe0e73cca47441e99fd6f0d (patch)
tree79639157821f207172e0fcc79a84445e426f73dd /Python/pylifecycle.c
parent871d26987533e81ab63af067e1fc96aa37a26bf7 (diff)
downloadcpython-27bd08273ce822a4dbe0e73cca47441e99fd6f0d.tar.gz
cpython-27bd08273ce822a4dbe0e73cca47441e99fd6f0d.zip
Revert "gh-128639: Don't assume one thread in subinterpreter finalization (gh-128640)" (gh-134256)
This reverts commit 9859791f9e116c827468f307ac0770286c975c8b. The original change broke the iOS and android buildbots, where the tests are run single-process.
Diffstat (limited to 'Python/pylifecycle.c')
-rw-r--r--Python/pylifecycle.c56
1 files changed, 28 insertions, 28 deletions
diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c
index b6bc2ea5211..8394245d373 100644
--- a/Python/pylifecycle.c
+++ b/Python/pylifecycle.c
@@ -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));
+
+ /* 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);
}
-
- /* Enter the subinterpreter. */
- _PyThreadState_Attach(tstate);
+ tstate = _PyThreadState_NewBound(interp, _PyThreadState_WHENCE_FINI);
/* Destroy the subinterpreter. */
+ _PyThreadState_Attach(tstate);
Py_EndInterpreter(tstate);
assert(_PyThreadState_GET() == NULL);