gh-59956: Clarify Runtime State Status Expectations (gh-101308)

A PyThreadState can be in one of many states in its lifecycle, represented by some status value.  Those statuses haven't been particularly clear, so we're addressing that here.  Specifically:

* made the distinct lifecycle statuses clear on PyThreadState
* identified expectations of how various lifecycle-related functions relate to status
* noted the various places where those expectations don't match the actual behavior

At some point we'll need to address the mismatches.

(This change also includes some cleanup.)

https://github.com/python/cpython/issues/59956
This commit is contained in:
Eric Snow 2023-01-30 12:07:48 -07:00 committed by GitHub
parent ea232716d3
commit e11fc032a7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 426 additions and 176 deletions

View file

@ -696,10 +696,11 @@ pycore_create_interpreter(_PyRuntimeState *runtime,
const _PyInterpreterConfig config = _PyInterpreterConfig_LEGACY_INIT;
init_interp_settings(interp, &config);
PyThreadState *tstate = PyThreadState_New(interp);
PyThreadState *tstate = _PyThreadState_New(interp);
if (tstate == NULL) {
return _PyStatus_ERR("can't make first thread");
}
_PyThreadState_Bind(tstate);
(void) PyThreadState_Swap(tstate);
status = init_interp_create_gil(tstate);
@ -1821,6 +1822,11 @@ Py_FinalizeEx(void)
/* Get current thread state and interpreter pointer */
PyThreadState *tstate = _PyRuntimeState_GetThreadState(runtime);
// XXX assert(_Py_IsMainInterpreter(tstate->interp));
// XXX assert(_Py_IsMainThread());
// Block some operations.
tstate->interp->finalizing = 1;
// Wrap up existing "threading"-module-created, non-daemon threads.
wait_for_thread_shutdown(tstate);
@ -1867,7 +1873,23 @@ Py_FinalizeEx(void)
_PyRuntimeState_SetFinalizing() has been called, no other Python thread
can take the GIL at this point: if they try, they will exit
immediately. */
_PyThreadState_DeleteExcept(runtime, tstate);
_PyThreadState_DeleteExcept(tstate);
/* At this point no Python code should be running at all.
The only thread state left should be the main thread of the main
interpreter (AKA tstate), in which this code is running right now.
There may be other OS threads running but none of them will have
thread states associated with them, nor will be able to create
new thread states.
Thus tstate is the only possible thread state from here on out.
It may still be used during finalization to run Python code as
needed or provide runtime state (e.g. sys.modules) but that will
happen sparingly. Furthermore, the order of finalization aims
to not need a thread (or interpreter) state as soon as possible.
*/
// XXX Make sure we are preventing the creating of any new thread states
// (or interpreters).
/* Flush sys.stdout and sys.stderr */
if (flush_std_files() < 0) {
@ -1958,6 +1980,20 @@ Py_FinalizeEx(void)
}
#endif /* Py_TRACE_REFS */
/* At this point there's almost no other Python code that will run,
nor interpreter state needed. The only possibility is the
finalizers of the objects stored on tstate (and tstate->interp),
which are triggered via finalize_interp_clear().
For now we operate as though none of those finalizers actually
need an operational thread state or interpreter. In reality,
those finalizers may rely on some part of tstate or
tstate->interp, and/or may raise exceptions
or otherwise fail.
*/
// XXX Do this sooner during finalization.
// XXX Ensure finalizer errors are handled properly.
finalize_interp_clear(tstate);
finalize_interp_delete(tstate->interp);
@ -2039,12 +2075,13 @@ new_interpreter(PyThreadState **tstate_p, const _PyInterpreterConfig *config)
return _PyStatus_OK();
}
PyThreadState *tstate = PyThreadState_New(interp);
PyThreadState *tstate = _PyThreadState_New(interp);
if (tstate == NULL) {
PyInterpreterState_Delete(interp);
*tstate_p = NULL;
return _PyStatus_OK();
}
_PyThreadState_Bind(tstate);
PyThreadState *save_tstate = PyThreadState_Swap(tstate);