aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/Python/pylifecycle.c
diff options
context:
space:
mode:
authorEric Snow <ericsnowcurrently@gmail.com>2023-01-30 12:07:48 -0700
committerGitHub <noreply@github.com>2023-01-30 12:07:48 -0700
commite11fc032a75d067d2167a21037722a770b9dfb51 (patch)
treea9e1b00b661f5ccc131f62f161d2963737bcf03c /Python/pylifecycle.c
parentea232716d3de1675478db3a302629ba43194c967 (diff)
downloadcpython-e11fc032a75d067d2167a21037722a770b9dfb51.tar.gz
cpython-e11fc032a75d067d2167a21037722a770b9dfb51.zip
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
Diffstat (limited to 'Python/pylifecycle.c')
-rw-r--r--Python/pylifecycle.c43
1 files changed, 40 insertions, 3 deletions
diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c
index 5ef2d3f6aa7..a8a8e7f3d84 100644
--- a/Python/pylifecycle.c
+++ b/Python/pylifecycle.c
@@ -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);