diff options
author | Peter Bierma <zintensitydev@gmail.com> | 2025-05-19 14:22:05 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-05-19 12:22:05 -0600 |
commit | 27bd08273ce822a4dbe0e73cca47441e99fd6f0d (patch) | |
tree | 79639157821f207172e0fcc79a84445e426f73dd /Python/pylifecycle.c | |
parent | 871d26987533e81ab63af067e1fc96aa37a26bf7 (diff) | |
download | cpython-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.c | 56 |
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); |