summaryrefslogtreecommitdiffstats
path: root/Python/pystate.c
diff options
context:
space:
mode:
authorEric Snow <ericsnowcurrently@gmail.com>2023-10-03 15:20:48 (GMT)
committerGitHub <noreply@github.com>2023-10-03 15:20:48 (GMT)
commitf5198b09e16bca1886f8245fa88203d07d51ec11 (patch)
treee2032653aee5b6e2df560f6a37eca5a957d1fb6f /Python/pystate.c
parent4227bfa8b273207a2b882f7d69c8ac49c3d2b57d (diff)
downloadcpython-f5198b09e16bca1886f8245fa88203d07d51ec11.zip
cpython-f5198b09e16bca1886f8245fa88203d07d51ec11.tar.gz
cpython-f5198b09e16bca1886f8245fa88203d07d51ec11.tar.bz2
gh-109860: Use a New Thread State When Switching Interpreters, When Necessary (gh-110245)
In a few places we switch to another interpreter without knowing if it has a thread state associated with the current thread. For the main interpreter there wasn't much of a problem, but for subinterpreters we were *mostly* okay re-using the tstate created with the interpreter (located via PyInterpreterState_ThreadHead()). There was a good chance that tstate wasn't actually in use by another thread. However, there are no guarantees of that. Furthermore, re-using an already used tstate is currently fragile. To address this, now we create a new thread state in each of those places and use it. One consequence of this change is that PyInterpreterState_ThreadHead() may not return NULL (though that won't happen for the main interpreter).
Diffstat (limited to 'Python/pystate.c')
-rw-r--r--Python/pystate.c78
1 files changed, 60 insertions, 18 deletions
diff --git a/Python/pystate.c b/Python/pystate.c
index fe056bf..25a957c 100644
--- a/Python/pystate.c
+++ b/Python/pystate.c
@@ -1094,9 +1094,7 @@ _PyInterpreterState_DeleteExceptMain(_PyRuntimeState *runtime)
int
_PyInterpreterState_SetRunningMain(PyInterpreterState *interp)
{
- if (interp->threads.main != NULL) {
- PyErr_SetString(PyExc_RuntimeError,
- "interpreter already running");
+ if (_PyInterpreterState_FailIfRunningMain(interp) < 0) {
return -1;
}
PyThreadState *tstate = current_fast_get(&_PyRuntime);
@@ -1113,7 +1111,20 @@ _PyInterpreterState_SetRunningMain(PyInterpreterState *interp)
void
_PyInterpreterState_SetNotRunningMain(PyInterpreterState *interp)
{
- assert(interp->threads.main == current_fast_get(&_PyRuntime));
+ PyThreadState *tstate = interp->threads.main;
+ assert(tstate == current_fast_get(&_PyRuntime));
+
+ if (tstate->on_delete != NULL) {
+ // The threading module was imported for the first time in this
+ // thread, so it was set as threading._main_thread. (See gh-75698.)
+ // The thread has finished running the Python program so we mark
+ // the thread object as finished.
+ assert(tstate->_whence != _PyThreadState_WHENCE_THREADING);
+ tstate->on_delete(tstate->on_delete_data);
+ tstate->on_delete = NULL;
+ tstate->on_delete_data = NULL;
+ }
+
interp->threads.main = NULL;
}
@@ -1123,6 +1134,17 @@ _PyInterpreterState_IsRunningMain(PyInterpreterState *interp)
return (interp->threads.main != NULL);
}
+int
+_PyInterpreterState_FailIfRunningMain(PyInterpreterState *interp)
+{
+ if (interp->threads.main != NULL) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "interpreter already running");
+ return -1;
+ }
+ return 0;
+}
+
//----------
// accessors
@@ -1183,8 +1205,10 @@ _PyInterpreterState_IDDecref(PyInterpreterState *interp)
PyThread_release_lock(interp->id_mutex);
if (refcount == 0 && interp->requires_idref) {
- // XXX Using the "head" thread isn't strictly correct.
- PyThreadState *tstate = PyInterpreterState_ThreadHead(interp);
+ PyThreadState *tstate = _PyThreadState_New(interp,
+ _PyThreadState_WHENCE_INTERP);
+ _PyThreadState_Bind(tstate);
+
// XXX Possible GILState issues?
PyThreadState *save_tstate = _PyThreadState_Swap(runtime, tstate);
Py_EndInterpreter(tstate);
@@ -1338,7 +1362,14 @@ free_threadstate(PyThreadState *tstate)
{
// The initial thread state of the interpreter is allocated
// as part of the interpreter state so should not be freed.
- if (tstate != &tstate->interp->_initial_thread) {
+ if (tstate == &tstate->interp->_initial_thread) {
+ // Restore to _PyThreadState_INIT.
+ tstate = &tstate->interp->_initial_thread;
+ memcpy(tstate,
+ &initial._main_interpreter._initial_thread,
+ sizeof(*tstate));
+ }
+ else {
PyMem_RawFree(tstate);
}
}
@@ -1353,7 +1384,7 @@ free_threadstate(PyThreadState *tstate)
static void
init_threadstate(PyThreadState *tstate,
- PyInterpreterState *interp, uint64_t id)
+ PyInterpreterState *interp, uint64_t id, int whence)
{
if (tstate->_status.initialized) {
Py_FatalError("thread state already initialized");
@@ -1366,6 +1397,10 @@ init_threadstate(PyThreadState *tstate,
assert(tstate->next == NULL);
assert(tstate->prev == NULL);
+ assert(tstate->_whence == _PyThreadState_WHENCE_NOTSET);
+ assert(whence >= 0 && whence <= _PyThreadState_WHENCE_EXEC);
+ tstate->_whence = whence;
+
assert(id > 0);
tstate->id = id;
@@ -1395,8 +1430,6 @@ add_threadstate(PyInterpreterState *interp, PyThreadState *tstate,
PyThreadState *next)
{
assert(interp->threads.head != tstate);
- assert((next != NULL && tstate->id != 1) ||
- (next == NULL && tstate->id == 1));
if (next != NULL) {
assert(next->prev == NULL || next->prev == tstate);
next->prev = tstate;
@@ -1407,7 +1440,7 @@ add_threadstate(PyInterpreterState *interp, PyThreadState *tstate,
}
static PyThreadState *
-new_threadstate(PyInterpreterState *interp)
+new_threadstate(PyInterpreterState *interp, int whence)
{
PyThreadState *tstate;
_PyRuntimeState *runtime = interp->runtime;
@@ -1430,10 +1463,10 @@ new_threadstate(PyInterpreterState *interp)
PyThreadState *old_head = interp->threads.head;
if (old_head == NULL) {
// It's the interpreter's initial thread state.
- assert(id == 1);
used_newtstate = 0;
tstate = &interp->_initial_thread;
}
+ // XXX Re-use interp->_initial_thread if not in use?
else {
// Every valid interpreter must have at least one thread.
assert(id > 1);
@@ -1446,7 +1479,7 @@ new_threadstate(PyInterpreterState *interp)
sizeof(*tstate));
}
- init_threadstate(tstate, interp, id);
+ init_threadstate(tstate, interp, id, whence);
add_threadstate(interp, tstate, old_head);
HEAD_UNLOCK(runtime);
@@ -1460,7 +1493,8 @@ new_threadstate(PyInterpreterState *interp)
PyThreadState *
PyThreadState_New(PyInterpreterState *interp)
{
- PyThreadState *tstate = new_threadstate(interp);
+ PyThreadState *tstate = new_threadstate(interp,
+ _PyThreadState_WHENCE_UNKNOWN);
if (tstate) {
bind_tstate(tstate);
// This makes sure there's a gilstate tstate bound
@@ -1474,16 +1508,16 @@ PyThreadState_New(PyInterpreterState *interp)
// This must be followed by a call to _PyThreadState_Bind();
PyThreadState *
-_PyThreadState_New(PyInterpreterState *interp)
+_PyThreadState_New(PyInterpreterState *interp, int whence)
{
- return new_threadstate(interp);
+ return new_threadstate(interp, whence);
}
// We keep this for stable ABI compabibility.
PyAPI_FUNC(PyThreadState*)
_PyThreadState_Prealloc(PyInterpreterState *interp)
{
- return _PyThreadState_New(interp);
+ return _PyThreadState_New(interp, _PyThreadState_WHENCE_UNKNOWN);
}
// We keep this around for (accidental) stable ABI compatibility.
@@ -1580,6 +1614,12 @@ PyThreadState_Clear(PyThreadState *tstate)
Py_CLEAR(tstate->context);
if (tstate->on_delete != NULL) {
+ // For the "main" thread of each interpreter, this is meant
+ // to be done in _PyInterpreterState_SetNotRunningMain().
+ // That leaves threads created by the threading module,
+ // and any threads killed by forking.
+ // However, we also accommodate "main" threads that still
+ // don't call _PyInterpreterState_SetNotRunningMain() yet.
tstate->on_delete(tstate->on_delete_data);
}
@@ -2240,7 +2280,9 @@ PyGILState_Ensure(void)
int has_gil;
if (tcur == NULL) {
/* Create a new Python thread state for this thread */
- tcur = new_threadstate(runtime->gilstate.autoInterpreterState);
+ // XXX Use PyInterpreterState_EnsureThreadState()?
+ tcur = new_threadstate(runtime->gilstate.autoInterpreterState,
+ _PyThreadState_WHENCE_GILSTATE);
if (tcur == NULL) {
Py_FatalError("Couldn't create thread-state for new thread");
}