From 4e88c2b6d1b953b9ee9ea7fc21e1ed192b973df5 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Mon, 25 Sep 2023 17:03:47 -0600 Subject: [PATCH 01/21] Always wait for threads in subinterpreters. --- Lib/test/test_threading.py | 50 ++++++++++++++++++++++++++++++++++++++ Lib/threading.py | 3 ++- Modules/_threadmodule.c | 16 +++++++++++- 3 files changed, 67 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py index 6465a446565844..3e435b840bacbd 100644 --- a/Lib/test/test_threading.py +++ b/Lib/test/test_threading.py @@ -26,6 +26,11 @@ from test import lock_tests from test import support +try: + from test.support import interpreters +except ModuleNotFoundError: + interpreters = None + threading_helper.requires_working_threading(module=True) # Between fork() and exec(), only async-safe functions are allowed (issues @@ -35,6 +40,12 @@ platforms_to_skip = ('netbsd5', 'hp-ux11') +def requires_subinterpreters(meth): + """Decorator to skip a test if subinterpreters are not supported.""" + return unittest.skipIf(interpreters is None, + 'subinterpreters required')(meth) + + def restore_default_excepthook(testcase): testcase.addCleanup(setattr, threading, 'excepthook', threading.excepthook) threading.excepthook = threading.__excepthook__ @@ -1298,6 +1309,45 @@ def f(): # The thread was joined properly. self.assertEqual(os.read(r, 1), b"x") + @requires_subinterpreters + def test_threads_join_with_no_main(self): +# r_thread, w_thread = self.pipe() + r_interp, w_interp = self.pipe() + + INTERP = b'I' + FINI = b'F' + DONE = b'D' + + interp = interpreters.create() + interp.run(f"""if True: + import os + import threading + import time + + done = False + + def notify_fini(): + global done + done = True + os.write({w_interp}, {FINI!r}) + t.join() + threading._register_atexit(notify_fini) + + def task(): + while not done: + time.sleep(0.1) + os.write({w_interp}, {DONE!r}) + t = threading.Thread(target=task) + t.start() + + os.write({w_interp}, {INTERP!r}) + """) + interp.close() + + self.assertEqual(os.read(r_inerp, 1), INTERP) + self.assertEqual(os.read(r_inerp, 1), FINI) + self.assertEqual(os.read(r_inerp, 1), DONE) + @cpython_only def test_daemon_threads_fatal_error(self): subinterp_code = f"""if 1: diff --git a/Lib/threading.py b/Lib/threading.py index f6bbdb0d0c80ee..dafe91059dbb55 100644 --- a/Lib/threading.py +++ b/Lib/threading.py @@ -38,6 +38,7 @@ _allocate_lock = _thread.allocate_lock _set_sentinel = _thread._set_sentinel get_ident = _thread.get_ident +_is_main_interpreter = _thread._is_main_interpreter try: get_native_id = _thread.get_native_id _HAVE_THREAD_NATIVE_ID = True @@ -1567,7 +1568,7 @@ def _shutdown(): # the main thread's tstate_lock - that won't happen until the interpreter # is nearly dead. So we release it here. Note that just calling _stop() # isn't enough: other threads may already be waiting on _tstate_lock. - if _main_thread._is_stopped: + if _main_thread._is_stopped and _is_main_interpreter(): # _shutdown() was already called return diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c index 7692bacccc4909..8ec91a07d2ecdc 100644 --- a/Modules/_threadmodule.c +++ b/Modules/_threadmodule.c @@ -1590,6 +1590,18 @@ PyDoc_STRVAR(excepthook_doc, \n\ Handle uncaught Thread.run() exception."); +static PyObject * +thread__is_main_interpreter(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + PyInterpreterState *interp = _PyInterpreterState_GET(); + return PyBool_FromLong(_Py_IsMainInterpreter(interp)); +} + +PyDoc_STRVAR(thread__is_main_interpreter_doc, +"_is_main_interpreter()\n\ +\n\ +Return True if the current interpreter is the main Python interpreter."); + static PyMethodDef thread_methods[] = { {"start_new_thread", (PyCFunction)thread_PyThread_start_new_thread, METH_VARARGS, start_new_doc}, @@ -1619,8 +1631,10 @@ static PyMethodDef thread_methods[] = { METH_VARARGS, stack_size_doc}, {"_set_sentinel", thread__set_sentinel, METH_NOARGS, _set_sentinel_doc}, - {"_excepthook", thread_excepthook, + {"_excepthook", thread_excepthook, METH_O, excepthook_doc}, + {"_is_main_interpreter", thread__is_main_interpreter, + METH_NOARGS, thread__is_main_interpreter_doc}, {NULL, NULL} /* sentinel */ }; From d08ac68ded822c4043690523c72ea82afc6fd202 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 26 Sep 2023 10:25:24 -0600 Subject: [PATCH 02/21] Add PyInterpreterState_*RunningMain(). --- Include/cpython/pystate.h | 6 +++ Include/internal/pycore_interp.h | 2 + Modules/_xxsubinterpretersmodule.c | 67 +++++++++------------------- Python/pystate.c | 71 ++++++++++++++++++++++++++++++ 4 files changed, 100 insertions(+), 46 deletions(-) diff --git a/Include/cpython/pystate.h b/Include/cpython/pystate.h index 5e184d0ca0944b..36cb4de229755e 100644 --- a/Include/cpython/pystate.h +++ b/Include/cpython/pystate.h @@ -211,6 +211,12 @@ struct _ts { * if it is NULL. */ PyAPI_FUNC(PyThreadState *) _PyThreadState_UncheckedGet(void); +PyAPI_FUNC(int) PyInterpreterState_SetRunningMain(PyInterpreterState *); +PyAPI_FUNC(void) PyInterpreterState_SetNotRunningMain(PyInterpreterState *); +PyAPI_FUNC(int) PyInterpreterState_IsRunningMain(PyInterpreterState *); +PyAPI_FUNC(int) PyThreadState_IsRunning(PyThreadState *); + + // Disable tracing and profiling. PyAPI_FUNC(void) PyThreadState_EnterTracing(PyThreadState *tstate); diff --git a/Include/internal/pycore_interp.h b/Include/internal/pycore_interp.h index ba5764e943e676..f8e70a3a01aaed 100644 --- a/Include/internal/pycore_interp.h +++ b/Include/internal/pycore_interp.h @@ -73,6 +73,8 @@ struct _is { uint64_t next_unique_id; /* The linked list of threads, newest first. */ PyThreadState *head; + /* The thread currently executing in the __main__ module, if any. */ + PyThreadState *main; /* Used in Modules/_threadmodule.c. */ long count; /* Support for runtime thread stack size tuning. diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c index 1ddf64909bf18a..e57c87113aebcd 100644 --- a/Modules/_xxsubinterpretersmodule.c +++ b/Modules/_xxsubinterpretersmodule.c @@ -312,7 +312,8 @@ _sharedexception_bind(PyObject *exc, _sharedexception *sharedexc) } static void -_sharedexception_apply(_sharedexception *exc, PyObject *wrapperclass) +_sharedexception_apply(_sharedexception *exc, PyObject *wrapperclass, + PyInterpreterState *interp) { if (exc->name != NULL) { if (exc->msg != NULL) { @@ -358,41 +359,14 @@ exceptions_init(PyObject *mod) } static int -_is_running(PyInterpreterState *interp) -{ - PyThreadState *tstate = PyInterpreterState_ThreadHead(interp); - if (PyThreadState_Next(tstate) != NULL) { - PyErr_SetString(PyExc_RuntimeError, - "interpreter has more than one thread"); - return -1; - } - - assert(!PyErr_Occurred()); - struct _PyInterpreterFrame *frame = tstate->current_frame; - if (frame == NULL) { - return 0; - } - return 1; -} - -static int -_ensure_not_running(PyInterpreterState *interp) +_run_script(PyInterpreterState *interp, const char *codestr, + _sharedns *shared, _sharedexception *sharedexc) { - int is_running = _is_running(interp); - if (is_running < 0) { - return -1; - } - if (is_running) { - PyErr_Format(PyExc_RuntimeError, "interpreter already running"); + if (PyInterpreterState_SetRunningMain(interp) < 0) { + // We skip going through the shared exception. return -1; } - return 0; -} -static int -_run_script(PyInterpreterState *interp, const char *codestr, - _sharedns *shared, _sharedexception *sharedexc) -{ PyObject *excval = NULL; PyObject *main_mod = PyUnstable_InterpreterState_GetMainModule(interp); if (main_mod == NULL) { @@ -415,6 +389,7 @@ _run_script(PyInterpreterState *interp, const char *codestr, // Run the string (see PyRun_SimpleStringFlags). PyObject *result = PyRun_StringFlags(codestr, Py_file_input, ns, ns, NULL); + PyInterpreterState_SetNotRunningMain(interp); Py_DECREF(ns); if (result == NULL) { goto error; @@ -437,6 +412,7 @@ _run_script(PyInterpreterState *interp, const char *codestr, } Py_XDECREF(excval); assert(!PyErr_Occurred()); + PyInterpreterState_SetNotRunningMain(interp); return -1; } @@ -444,9 +420,6 @@ static int _run_script_in_interpreter(PyObject *mod, PyInterpreterState *interp, const char *codestr, PyObject *shareables) { - if (_ensure_not_running(interp) < 0) { - return -1; - } module_state *state = get_module_state(mod); _sharedns *shared = _get_shared_ns(shareables); @@ -456,9 +429,11 @@ _run_script_in_interpreter(PyObject *mod, PyInterpreterState *interp, // Switch to interpreter. PyThreadState *save_tstate = NULL; - if (interp != PyInterpreterState_Get()) { + PyThreadState *tcur = PyThreadState_Get(); + PyThreadState *tstate = tcur; + if (interp != tstate->interp) { // XXX Using the "head" thread isn't strictly correct. - PyThreadState *tstate = PyInterpreterState_ThreadHead(interp); + tstate = PyInterpreterState_ThreadHead(interp); // XXX Possible GILState issues? save_tstate = PyThreadState_Swap(tstate); } @@ -475,11 +450,13 @@ _run_script_in_interpreter(PyObject *mod, PyInterpreterState *interp, // Propagate any exception out to the caller. if (exc.name != NULL) { assert(state != NULL); - _sharedexception_apply(&exc, state->RunFailedError); + _sharedexception_apply(&exc, state->RunFailedError, interp); } else if (result != 0) { - // We were unable to allocate a shared exception. - PyErr_NoMemory(); + if (!PyErr_Occurred()) { + // We were unable to allocate a shared exception. + PyErr_NoMemory(); + } } if (shared != NULL) { @@ -574,7 +551,8 @@ interp_destroy(PyObject *self, PyObject *args, PyObject *kwds) // Ensure the interpreter isn't running. /* XXX We *could* support destroying a running interpreter but aren't going to worry about it for now. */ - if (_ensure_not_running(interp) < 0) { + if (PyInterpreterState_IsRunningMain(interp)) { + PyErr_Format(PyExc_RuntimeError, "interpreter running"); return NULL; } @@ -748,11 +726,7 @@ interp_is_running(PyObject *self, PyObject *args, PyObject *kwds) if (interp == NULL) { return NULL; } - int is_running = _is_running(interp); - if (is_running < 0) { - return NULL; - } - if (is_running) { + if (PyInterpreterState_IsRunningMain(interp)) { Py_RETURN_TRUE; } Py_RETURN_FALSE; @@ -763,6 +737,7 @@ PyDoc_STRVAR(is_running_doc, \n\ Return whether or not the identified interpreter is running."); + static PyMethodDef module_functions[] = { {"create", _PyCFunction_CAST(interp_create), METH_VARARGS | METH_KEYWORDS, create_doc}, diff --git a/Python/pystate.c b/Python/pystate.c index dcc6c112215b30..487ea51244e228 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -1095,6 +1095,50 @@ _PyInterpreterState_DeleteExceptMain(_PyRuntimeState *runtime) #endif +int +PyInterpreterState_SetRunningMain(PyInterpreterState *interp) +{ + if (interp->threads.main != NULL) { + PyErr_SetString(PyExc_RuntimeError, + "interpreter already running"); + return -1; + } + PyThreadState *tstate = current_fast_get(&_PyRuntime); + _Py_EnsureTstateNotNULL(tstate); + if (tstate->interp != interp) { + PyErr_SetString(PyExc_RuntimeError, + "current tstate has wrong interpreter"); + return -1; + } + interp->threads.main = tstate; + return 0; +} + +void +PyInterpreterState_SetNotRunningMain(PyInterpreterState *interp) +{ + assert(interp->threads.main == current_fast_get(&_PyRuntime)); + interp->threads.main = NULL; +} + +static int interpreter_exists(PyInterpreterState *); + +int +PyInterpreterState_IsRunningMain(PyInterpreterState *interp) +{ + HEAD_LOCK(interp->runtime); + int exists = interpreter_exists(interp); + HEAD_UNLOCK(interp->runtime); + if (!exists) { + return 0; + } + if (interp->threads.main == NULL) { + return 0; + } + return PyThreadState_IsRunning(interp->threads.main); +} + + //---------- // accessors //---------- @@ -1204,6 +1248,22 @@ PyInterpreterState_GetDict(PyInterpreterState *interp) // look up an interpreter state //----------------------------- +/* This must be called with the "head" lock held. */ +static int +interpreter_exists(PyInterpreterState *interp) +{ + PyInterpreterState *actual = interp->runtime->interpreters.head; + while (actual != NULL) { + if (actual == interp) { + return 1; + } + actual = actual->next; + } + // It's a dangling pointer. + return 0; +} + + /* Return the interpreter associated with the current OS thread. The GIL must be held. @@ -1683,6 +1743,13 @@ _PyThreadState_DeleteExcept(PyThreadState *tstate) } +int +PyThreadState_IsRunning(PyThreadState *tstate) +{ + return tstate->current_frame != NULL; +} + + //---------- // accessors //---------- @@ -2805,6 +2872,10 @@ _register_builtins_for_crossinterpreter_data(struct _xidregistry *xidregistry) } +/*************/ +/* Other API */ +/*************/ + _PyFrameEvalFunction _PyInterpreterState_GetEvalFrameFunc(PyInterpreterState *interp) { From 3b07482e17f89078775286058e5c39b22abecb51 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 26 Sep 2023 10:27:44 -0600 Subject: [PATCH 03/21] Drop interpreter_exists(). --- Python/pystate.c | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/Python/pystate.c b/Python/pystate.c index 487ea51244e228..6da24ebb4158cd 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -1121,17 +1121,9 @@ PyInterpreterState_SetNotRunningMain(PyInterpreterState *interp) interp->threads.main = NULL; } -static int interpreter_exists(PyInterpreterState *); - int PyInterpreterState_IsRunningMain(PyInterpreterState *interp) { - HEAD_LOCK(interp->runtime); - int exists = interpreter_exists(interp); - HEAD_UNLOCK(interp->runtime); - if (!exists) { - return 0; - } if (interp->threads.main == NULL) { return 0; } @@ -1248,22 +1240,6 @@ PyInterpreterState_GetDict(PyInterpreterState *interp) // look up an interpreter state //----------------------------- -/* This must be called with the "head" lock held. */ -static int -interpreter_exists(PyInterpreterState *interp) -{ - PyInterpreterState *actual = interp->runtime->interpreters.head; - while (actual != NULL) { - if (actual == interp) { - return 1; - } - actual = actual->next; - } - // It's a dangling pointer. - return 0; -} - - /* Return the interpreter associated with the current OS thread. The GIL must be held. From 70c4753f8574ffdbf8df76869a223effb9df4386 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 26 Sep 2023 10:29:46 -0600 Subject: [PATCH 04/21] Drop PyThreadState_IsRunning(). --- Include/cpython/pystate.h | 1 - Python/pystate.c | 12 +----------- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/Include/cpython/pystate.h b/Include/cpython/pystate.h index 36cb4de229755e..6b6090b0808752 100644 --- a/Include/cpython/pystate.h +++ b/Include/cpython/pystate.h @@ -214,7 +214,6 @@ PyAPI_FUNC(PyThreadState *) _PyThreadState_UncheckedGet(void); PyAPI_FUNC(int) PyInterpreterState_SetRunningMain(PyInterpreterState *); PyAPI_FUNC(void) PyInterpreterState_SetNotRunningMain(PyInterpreterState *); PyAPI_FUNC(int) PyInterpreterState_IsRunningMain(PyInterpreterState *); -PyAPI_FUNC(int) PyThreadState_IsRunning(PyThreadState *); // Disable tracing and profiling. diff --git a/Python/pystate.c b/Python/pystate.c index 6da24ebb4158cd..2e94d090418a0a 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -1124,10 +1124,7 @@ PyInterpreterState_SetNotRunningMain(PyInterpreterState *interp) int PyInterpreterState_IsRunningMain(PyInterpreterState *interp) { - if (interp->threads.main == NULL) { - return 0; - } - return PyThreadState_IsRunning(interp->threads.main); + return (interp->threads.main != NULL); } @@ -1719,13 +1716,6 @@ _PyThreadState_DeleteExcept(PyThreadState *tstate) } -int -PyThreadState_IsRunning(PyThreadState *tstate) -{ - return tstate->current_frame != NULL; -} - - //---------- // accessors //---------- From 678cb3b03b717ff253c378e67c95d75fc86a626a Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 26 Sep 2023 11:04:34 -0600 Subject: [PATCH 05/21] Add a TODO. --- Lib/threading.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/threading.py b/Lib/threading.py index dafe91059dbb55..0470d34097fcdb 100644 --- a/Lib/threading.py +++ b/Lib/threading.py @@ -1621,6 +1621,7 @@ def main_thread(): In normal conditions, the main thread is the thread from which the Python interpreter was started. """ + # XXX Figure this out for subinterpreters. (See gh-75698.) return _main_thread # get thread-local implementation, either from the thread From 5be5a070dc9c1540dbd1fd13cbbaae2c8d364dbe Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 26 Sep 2023 12:31:01 -0600 Subject: [PATCH 06/21] For now use the earliest thread state. --- Modules/_xxsubinterpretersmodule.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c index e57c87113aebcd..21c124c4b5a4a3 100644 --- a/Modules/_xxsubinterpretersmodule.c +++ b/Modules/_xxsubinterpretersmodule.c @@ -434,6 +434,10 @@ _run_script_in_interpreter(PyObject *mod, PyInterpreterState *interp, if (interp != tstate->interp) { // XXX Using the "head" thread isn't strictly correct. tstate = PyInterpreterState_ThreadHead(interp); + assert(tstate != NULL); + while(tstate->next != NULL) { + tstate = tstate->next; + } // XXX Possible GILState issues? save_tstate = PyThreadState_Swap(tstate); } @@ -558,6 +562,10 @@ interp_destroy(PyObject *self, PyObject *args, PyObject *kwds) // Destroy the interpreter. PyThreadState *tstate = PyInterpreterState_ThreadHead(interp); + assert(tstate != NULL); + while(tstate->next != NULL) { + tstate = tstate->next; + } // XXX Possible GILState issues? PyThreadState *save_tstate = PyThreadState_Swap(tstate); Py_EndInterpreter(tstate); From 81a1e0b3de8916455a97a0e39ae09c2893af463f Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 26 Sep 2023 12:51:57 -0600 Subject: [PATCH 07/21] Add tests. --- Lib/test/test_interpreters.py | 97 +++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/Lib/test/test_interpreters.py b/Lib/test/test_interpreters.py index 90932c0f66f38f..db22ee61847443 100644 --- a/Lib/test/test_interpreters.py +++ b/Lib/test/test_interpreters.py @@ -257,6 +257,16 @@ def test_subinterpreter(self): self.assertTrue(interp.is_running()) self.assertFalse(interp.is_running()) + def test_finished(self): + r, w = os.pipe() + interp = interpreters.create() + interp.run(f"""if True: + import os + os.write({w}, b'x') + """) + self.assertFalse(interp.is_running()) + self.assertEqual(os.read(r, 1), b'x') + def test_from_subinterpreter(self): interp = interpreters.create() out = _run_output(interp, dedent(f""" @@ -284,6 +294,31 @@ def test_bad_id(self): with self.assertRaises(ValueError): interp.is_running() + def test_with_only_background_threads(self): + r_interp, w_interp = os.pipe() + r_thread, w_thread = os.pipe() + + DONE = b'D' + FINISHED = b'F' + + interp = interpreters.create() + interp.run(f"""if True: + import os + import threading + + def task(): + v = os.read({r_thread}, 1) + assert v == {DONE!r} + os.write({w_interp}, {FINISHED!r}) + t = threading.Thread(target=task) + t.start() + """) + self.assertFalse(interp.is_running()) + + os.write(w_thread, DONE) + interp.run('t.join()') + self.assertEqual(os.read(r_interp, 1), FINISHED) + class TestInterpreterClose(TestBase): @@ -385,6 +420,37 @@ def test_still_running(self): interp.close() self.assertTrue(interp.is_running()) + def test_subthreads_still_running(self): + r_interp, w_interp = os.pipe() + r_thread, w_thread = os.pipe() + + FINISHED = b'F' + + interp = interpreters.create() + interp.run(f"""if True: + import os + import threading + import time + + done = False + + def notify_fini(): + global done + done = True + t.join() + threading._register_atexit(notify_fini) + + def task(): + while not done: + time.sleep(0.1) + os.write({w_interp}, {FINISHED!r}) + t = threading.Thread(target=task) + t.start() + """) + interp.close() + + self.assertEqual(os.read(r_interp, 1), FINISHED) + class TestInterpreterRun(TestBase): @@ -461,6 +527,37 @@ def test_bytes_for_script(self): with self.assertRaises(TypeError): interp.run(b'print("spam")') + def test_with_background_threads_still_running(self): + r_interp, w_interp = os.pipe() + r_thread, w_thread = os.pipe() + + RAN = b'R' + DONE = b'D' + FINISHED = b'F' + + interp = interpreters.create() + interp.run(f"""if True: + import os + import threading + + def task(): + v = os.read({r_thread}, 1) + assert v == {DONE!r} + os.write({w_interp}, {FINISHED!r}) + t = threading.Thread(target=task) + t.start() + os.write({w_interp}, {RAN!r}) + """) + interp.run(f"""if True: + os.write({w_interp}, {RAN!r}) + """) + + os.write(w_thread, DONE) + interp.run('t.join()') + self.assertEqual(os.read(r_interp, 1), RAN) + self.assertEqual(os.read(r_interp, 1), RAN) + self.assertEqual(os.read(r_interp, 1), FINISHED) + # test_xxsubinterpreters covers the remaining Interpreter.run() behavior. From c3f497b672c8777bb1f0ac7a8a4553cab18cb3e7 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 26 Sep 2023 12:58:29 -0600 Subject: [PATCH 08/21] Mark the main interpreter as running __main__. --- Modules/main.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Modules/main.c b/Modules/main.c index 7f88c97207475b..a483418721609c 100644 --- a/Modules/main.c +++ b/Modules/main.c @@ -598,6 +598,9 @@ pymain_run_python(int *exitcode) pymain_header(config); + PyInterpreterState_SetRunningMain(interp); + assert(!PyErr_Occurred()); + if (config->run_command) { *exitcode = pymain_run_command(config->run_command); } @@ -621,6 +624,7 @@ pymain_run_python(int *exitcode) *exitcode = pymain_exit_err_print(); done: + PyInterpreterState_SetNotRunningMain(interp); Py_XDECREF(main_importer_path); } From effa5b42507de4ba541423b8d440e61392185863 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 26 Sep 2023 13:08:11 -0600 Subject: [PATCH 09/21] Call PyInterpreterState_SetNotRunningMain() in the right place. --- Modules/_xxsubinterpretersmodule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c index 21c124c4b5a4a3..3279d8b23662e0 100644 --- a/Modules/_xxsubinterpretersmodule.c +++ b/Modules/_xxsubinterpretersmodule.c @@ -389,7 +389,6 @@ _run_script(PyInterpreterState *interp, const char *codestr, // Run the string (see PyRun_SimpleStringFlags). PyObject *result = PyRun_StringFlags(codestr, Py_file_input, ns, ns, NULL); - PyInterpreterState_SetNotRunningMain(interp); Py_DECREF(ns); if (result == NULL) { goto error; @@ -397,6 +396,7 @@ _run_script(PyInterpreterState *interp, const char *codestr, else { Py_DECREF(result); // We throw away the result. } + PyInterpreterState_SetNotRunningMain(interp); *sharedexc = no_exception; return 0; From 41c54b0958244a7d6fb31472b31d430594945201 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 26 Sep 2023 13:22:31 -0600 Subject: [PATCH 10/21] Check PyInterpreterState_IsRunningMain() preemptively, for now. --- Modules/_xxsubinterpretersmodule.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c index 3279d8b23662e0..1f3e21120fd443 100644 --- a/Modules/_xxsubinterpretersmodule.c +++ b/Modules/_xxsubinterpretersmodule.c @@ -438,6 +438,16 @@ _run_script_in_interpreter(PyObject *mod, PyInterpreterState *interp, while(tstate->next != NULL) { tstate = tstate->next; } + // XXX Drop this redundant check once we stop re-using + // tstates that might already be in use. + if (PyInterpreterState_IsRunningMain(interp)) { + PyErr_SetString(PyExc_RuntimeError, + "interpreter already running"); + if (shared != NULL) { + _sharedns_free(shared); + } + return -1; + } // XXX Possible GILState issues? save_tstate = PyThreadState_Swap(tstate); } From 5fbd940737510ea9b2e2e61cd7b7ad6bcf67c481 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 26 Sep 2023 13:57:15 -0600 Subject: [PATCH 11/21] Add docs. --- Doc/c-api/init.rst | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/Doc/c-api/init.rst b/Doc/c-api/init.rst index 60f5c81cff572c..d499ef559c43b5 100644 --- a/Doc/c-api/init.rst +++ b/Doc/c-api/init.rst @@ -1115,6 +1115,42 @@ All of the following functions must be called after :c:func:`Py_Initialize`. .. versionadded:: 3.7 +.. c:function:: int PyInterpreterState_SetRunningMain(PyInterpreterState *interp) + + Indicate that current thread will be executing Python code against + the ``__main__`` module of the given interpreter. This is meant for + use in embedded applications or code that manages subinterpreters. + Calling this is appropriate right before running a script, + e.g. using :c:func:`PyRun_SimpleString`. + + If the interpreter is already running then this will raise + :exc:`RuntimeError`. + + This must be called in the thread where that will happen. + The caller must hold the GIL. Every call must have a corresponding + call to :c:func:`PyInterpreterState_UnsetRunningMain`. + + .. versionadded:: 3.13 + + +.. c:function:: void PyInterpreterState_UnsetRunningMain(PyInterpreterState *interp) + + Indicate that the current thread is done running code in the + interpreter's ``__main__`` module. This is the companion to + :c:func:`PyInterpreterState_SetRunningMain`. + + .. versionadded:: 3.13 + + +.. c:function:: int PyInterpreterState_IsRunningMain(PyInterpreterState *interp) + + Return true (nonzero) if the interpreter is currently running a script + in its ``__main__`` module, false (zero) if not. + See :c:func:`PyInterpreterState_SetRunningMain`. + + .. versionadded:: 3.13 + + .. c:function:: PyObject* PyInterpreterState_GetDict(PyInterpreterState *interp) Return a dictionary in which interpreter-specific data may be stored. From 8b54c2ee83eedc4dad81fae8b11544973414e7fd Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 26 Sep 2023 14:00:34 -0600 Subject: [PATCH 12/21] Add a NEWS entry. --- .../2023-09-26-14-00-25.gh-issue-105716.SUJkW1.rst | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-09-26-14-00-25.gh-issue-105716.SUJkW1.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-09-26-14-00-25.gh-issue-105716.SUJkW1.rst b/Misc/NEWS.d/next/Core and Builtins/2023-09-26-14-00-25.gh-issue-105716.SUJkW1.rst new file mode 100644 index 00000000000000..b35550fa650dcc --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-09-26-14-00-25.gh-issue-105716.SUJkW1.rst @@ -0,0 +1,3 @@ +Subinterpreters now correctly handle the case where they have threads +running in the background. Before, such threads would interfere with +cleaning up and destroying them, as well as prevent running another script. From 5d1df6150abd364afad2955dca6e7a1215300e43 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 27 Sep 2023 09:24:45 -0600 Subject: [PATCH 13/21] Fix a typo. --- Lib/test/test_threading.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py index 3e435b840bacbd..b1cdfc977b1046 100644 --- a/Lib/test/test_threading.py +++ b/Lib/test/test_threading.py @@ -1344,9 +1344,9 @@ def task(): """) interp.close() - self.assertEqual(os.read(r_inerp, 1), INTERP) - self.assertEqual(os.read(r_inerp, 1), FINI) - self.assertEqual(os.read(r_inerp, 1), DONE) + self.assertEqual(os.read(r_interp, 1), INTERP) + self.assertEqual(os.read(r_interp, 1), FINI) + self.assertEqual(os.read(r_interp, 1), DONE) @cpython_only def test_daemon_threads_fatal_error(self): From 1034a67096dcf3a9362df35c82ca7c9b24344f0d Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 27 Sep 2023 11:41:32 -0600 Subject: [PATCH 14/21] Update the docs. --- Doc/c-api/init.rst | 58 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 44 insertions(+), 14 deletions(-) diff --git a/Doc/c-api/init.rst b/Doc/c-api/init.rst index d499ef559c43b5..49d9413267cf1d 100644 --- a/Doc/c-api/init.rst +++ b/Doc/c-api/init.rst @@ -1117,37 +1117,67 @@ All of the following functions must be called after :c:func:`Py_Initialize`. .. c:function:: int PyInterpreterState_SetRunningMain(PyInterpreterState *interp) - Indicate that current thread will be executing Python code against - the ``__main__`` module of the given interpreter. This is meant for - use in embedded applications or code that manages subinterpreters. - Calling this is appropriate right before running a script, - e.g. using :c:func:`PyRun_SimpleString`. + Indicate that the current thread will be running the "main" Python + program (e.g. script) for the given interpreter. This typically + means executing Python code against the ``__main__`` module of + the interpreter, e.g. using :c:func:`PyRun_SimpleString`. - If the interpreter is already running then this will raise - :exc:`RuntimeError`. + Only one thread may be "main" at a time for each interpreter. + Thus, this should not be called in subthreads created by the program. + If the interpreter is already marked as running then this function + will raise :exc:`RuntimeError` (even if in the same thread where + it was called earlier). - This must be called in the thread where that will happen. - The caller must hold the GIL. Every call must have a corresponding - call to :c:func:`PyInterpreterState_UnsetRunningMain`. + Every call to this function must have a corresponding call to + :c:func:`PyInterpreterState_UnsetRunningMain`. The pairs of calls + may happen any number of times. + + Subthreads from a previous "main" execution may still be running + when this is called. Likewise, the interpreter is not required + to be in a pristine condition and may still hold state and objects + from an earlier "main". + + Calling this function is is not required before running Python code + or otherwise interacting with the runtime. + + The caller must hold the GIL. + + This function is intended for use in embedded applications + or by extension modules that manage subinterpreters. .. versionadded:: 3.13 .. c:function:: void PyInterpreterState_UnsetRunningMain(PyInterpreterState *interp) - Indicate that the current thread is done running code in the - interpreter's ``__main__`` module. This is the companion to + Indicate that the current thread is done running the "main" Python + program for the given interpreter. This is the companion to :c:func:`PyInterpreterState_SetRunningMain`. + Subthreads created by the program may still be running when this + is called. Likewise, the interpreter's state is neither reset nor + required to be reset. + + The caller must hold the GIL. + + This function is intended for use in embedded applications + or by extension modules that manage subinterpreters. + .. versionadded:: 3.13 .. c:function:: int PyInterpreterState_IsRunningMain(PyInterpreterState *interp) - Return true (nonzero) if the interpreter is currently running a script - in its ``__main__`` module, false (zero) if not. + Return true (nonzero) if the interpreter is currently running + a "main" Python program in one of its threads, false (zero) if not. See :c:func:`PyInterpreterState_SetRunningMain`. + The caller must hold the GIL. + + This function will typically only be useful for embedded applications + or extension modules that manage subinterpreters. For other cases + this will always return true. + .. versionadded:: 3.13 From d57729ed5218d32591fee3797aab2827619c8854 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 27 Sep 2023 12:10:44 -0600 Subject: [PATCH 15/21] Update the docs. --- Doc/c-api/init.rst | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/Doc/c-api/init.rst b/Doc/c-api/init.rst index 49d9413267cf1d..9137748f91dd56 100644 --- a/Doc/c-api/init.rst +++ b/Doc/c-api/init.rst @@ -1137,13 +1137,16 @@ All of the following functions must be called after :c:func:`Py_Initialize`. to be in a pristine condition and may still hold state and objects from an earlier "main". - Calling this function is is not required before running Python code - or otherwise interacting with the runtime. + Before running a Python program, there are often a number of + interactions with the runtime, sometimes even including running + non-"main" Python code. Calling this function first before doing + them is not necessary, as long as it is called before running the + program itself. The caller must hold the GIL. - This function is intended for use in embedded applications - or by extension modules that manage subinterpreters. + This function is intended for use in embedded applications or by + extension modules that manage subinterpreters. .. versionadded:: 3.13 @@ -1176,7 +1179,8 @@ All of the following functions must be called after :c:func:`Py_Initialize`. This function will typically only be useful for embedded applications or extension modules that manage subinterpreters. For other cases - this will always return true. + expect that this would always return true, since the application + or subinterpreters extension should have already set it. .. versionadded:: 3.13 From 4219e2c396eb6f382b7cfa7dbffabe60a2b5db98 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 27 Sep 2023 12:17:35 -0600 Subject: [PATCH 16/21] Update the docs. --- Doc/c-api/init.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Doc/c-api/init.rst b/Doc/c-api/init.rst index 9137748f91dd56..3e19e547d0205c 100644 --- a/Doc/c-api/init.rst +++ b/Doc/c-api/init.rst @@ -1175,6 +1175,10 @@ All of the following functions must be called after :c:func:`Py_Initialize`. a "main" Python program in one of its threads, false (zero) if not. See :c:func:`PyInterpreterState_SetRunningMain`. + This relates only to the thread where the program started, + not subthreads. An interpreter may still have subthreads running + even if this returns false. + The caller must hold the GIL. This function will typically only be useful for embedded applications From bfdace2a567a472f27dd33ea6fa21b2d3c34c847 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 27 Sep 2023 12:19:55 -0600 Subject: [PATCH 17/21] Drop a dead line. --- Lib/test/test_threading.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py index b1cdfc977b1046..3c7fa933645128 100644 --- a/Lib/test/test_threading.py +++ b/Lib/test/test_threading.py @@ -1311,7 +1311,6 @@ def f(): @requires_subinterpreters def test_threads_join_with_no_main(self): -# r_thread, w_thread = self.pipe() r_interp, w_interp = self.pipe() INTERP = b'I' From ef8af920862bf32b8def593ee8dedd186649f6c6 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 27 Sep 2023 12:33:55 -0600 Subject: [PATCH 18/21] Drop an unused parameter. --- Modules/_xxsubinterpretersmodule.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c index 1f3e21120fd443..8a3d751a508bb9 100644 --- a/Modules/_xxsubinterpretersmodule.c +++ b/Modules/_xxsubinterpretersmodule.c @@ -312,8 +312,7 @@ _sharedexception_bind(PyObject *exc, _sharedexception *sharedexc) } static void -_sharedexception_apply(_sharedexception *exc, PyObject *wrapperclass, - PyInterpreterState *interp) +_sharedexception_apply(_sharedexception *exc, PyObject *wrapperclass) { if (exc->name != NULL) { if (exc->msg != NULL) { @@ -464,7 +463,7 @@ _run_script_in_interpreter(PyObject *mod, PyInterpreterState *interp, // Propagate any exception out to the caller. if (exc.name != NULL) { assert(state != NULL); - _sharedexception_apply(&exc, state->RunFailedError, interp); + _sharedexception_apply(&exc, state->RunFailedError); } else if (result != 0) { if (!PyErr_Occurred()) { From 566cd08ce0cd1cdd37aa7aeeb2aa140af9aa8ed5 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 27 Sep 2023 12:34:37 -0600 Subject: [PATCH 19/21] Drop an unnecessary variable. --- Modules/_xxsubinterpretersmodule.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c index 8a3d751a508bb9..685d1d5e1ad4d6 100644 --- a/Modules/_xxsubinterpretersmodule.c +++ b/Modules/_xxsubinterpretersmodule.c @@ -428,11 +428,9 @@ _run_script_in_interpreter(PyObject *mod, PyInterpreterState *interp, // Switch to interpreter. PyThreadState *save_tstate = NULL; - PyThreadState *tcur = PyThreadState_Get(); - PyThreadState *tstate = tcur; - if (interp != tstate->interp) { + if (interp != PyInterpreterState_Get()) { // XXX Using the "head" thread isn't strictly correct. - tstate = PyInterpreterState_ThreadHead(interp); + PyThreadState *tstate = PyInterpreterState_ThreadHead(interp); assert(tstate != NULL); while(tstate->next != NULL) { tstate = tstate->next; From d14b87a3c31a6e6bb7c16c99a7b83a4904f3611c Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 27 Sep 2023 12:45:00 -0600 Subject: [PATCH 20/21] Update TODO comments. --- Modules/_xxsubinterpretersmodule.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c index 685d1d5e1ad4d6..c09dbc3c72e712 100644 --- a/Modules/_xxsubinterpretersmodule.c +++ b/Modules/_xxsubinterpretersmodule.c @@ -429,14 +429,18 @@ _run_script_in_interpreter(PyObject *mod, PyInterpreterState *interp, // Switch to interpreter. PyThreadState *save_tstate = NULL; if (interp != PyInterpreterState_Get()) { - // XXX Using the "head" thread isn't strictly correct. + // XXX gh-109860: Using the "head" thread isn't strictly correct. PyThreadState *tstate = PyInterpreterState_ThreadHead(interp); assert(tstate != NULL); + // Hack (until gh-109860): The interpreter's initial thread state + // is least likely to break. while(tstate->next != NULL) { tstate = tstate->next; } - // XXX Drop this redundant check once we stop re-using - // tstates that might already be in use. + // We must do this check before switching interpreters, so any + // exception gets raised in the right one. + // XXX gh-109860: Drop this redundant check once we stop + // re-using tstates that might already be in use. if (PyInterpreterState_IsRunningMain(interp)) { PyErr_SetString(PyExc_RuntimeError, "interpreter already running"); @@ -568,8 +572,11 @@ interp_destroy(PyObject *self, PyObject *args, PyObject *kwds) } // Destroy the interpreter. + // XXX gh-109860: Using the "head" thread isn't strictly correct. PyThreadState *tstate = PyInterpreterState_ThreadHead(interp); assert(tstate != NULL); + // Hack (until gh-109860): The interpreter's initial thread state + // is least likely to break. while(tstate->next != NULL) { tstate = tstate->next; } From 399859f5906ac4af410984c8d090850a0dd13644 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Mon, 2 Oct 2023 13:21:29 -0600 Subject: [PATCH 21/21] Do not expose the "Running" C-API (for now). --- Doc/c-api/init.rst | 74 ------------------------------ Include/cpython/pystate.h | 4 -- Include/internal/pycore_pystate.h | 5 ++ Modules/_xxsubinterpretersmodule.c | 13 +++--- Modules/main.c | 4 +- Python/pystate.c | 6 +-- 6 files changed, 17 insertions(+), 89 deletions(-) diff --git a/Doc/c-api/init.rst b/Doc/c-api/init.rst index 3e19e547d0205c..60f5c81cff572c 100644 --- a/Doc/c-api/init.rst +++ b/Doc/c-api/init.rst @@ -1115,80 +1115,6 @@ All of the following functions must be called after :c:func:`Py_Initialize`. .. versionadded:: 3.7 -.. c:function:: int PyInterpreterState_SetRunningMain(PyInterpreterState *interp) - - Indicate that the current thread will be running the "main" Python - program (e.g. script) for the given interpreter. This typically - means executing Python code against the ``__main__`` module of - the interpreter, e.g. using :c:func:`PyRun_SimpleString`. - - Only one thread may be "main" at a time for each interpreter. - Thus, this should not be called in subthreads created by the program. - If the interpreter is already marked as running then this function - will raise :exc:`RuntimeError` (even if in the same thread where - it was called earlier). - - Every call to this function must have a corresponding call to - :c:func:`PyInterpreterState_UnsetRunningMain`. The pairs of calls - may happen any number of times. - - Subthreads from a previous "main" execution may still be running - when this is called. Likewise, the interpreter is not required - to be in a pristine condition and may still hold state and objects - from an earlier "main". - - Before running a Python program, there are often a number of - interactions with the runtime, sometimes even including running - non-"main" Python code. Calling this function first before doing - them is not necessary, as long as it is called before running the - program itself. - - The caller must hold the GIL. - - This function is intended for use in embedded applications or by - extension modules that manage subinterpreters. - - .. versionadded:: 3.13 - - -.. c:function:: void PyInterpreterState_UnsetRunningMain(PyInterpreterState *interp) - - Indicate that the current thread is done running the "main" Python - program for the given interpreter. This is the companion to - :c:func:`PyInterpreterState_SetRunningMain`. - - Subthreads created by the program may still be running when this - is called. Likewise, the interpreter's state is neither reset nor - required to be reset. - - The caller must hold the GIL. - - This function is intended for use in embedded applications - or by extension modules that manage subinterpreters. - - .. versionadded:: 3.13 - - -.. c:function:: int PyInterpreterState_IsRunningMain(PyInterpreterState *interp) - - Return true (nonzero) if the interpreter is currently running - a "main" Python program in one of its threads, false (zero) if not. - See :c:func:`PyInterpreterState_SetRunningMain`. - - This relates only to the thread where the program started, - not subthreads. An interpreter may still have subthreads running - even if this returns false. - - The caller must hold the GIL. - - This function will typically only be useful for embedded applications - or extension modules that manage subinterpreters. For other cases - expect that this would always return true, since the application - or subinterpreters extension should have already set it. - - .. versionadded:: 3.13 - - .. c:function:: PyObject* PyInterpreterState_GetDict(PyInterpreterState *interp) Return a dictionary in which interpreter-specific data may be stored. diff --git a/Include/cpython/pystate.h b/Include/cpython/pystate.h index 6b6090b0808752..7e4c57efc7c00c 100644 --- a/Include/cpython/pystate.h +++ b/Include/cpython/pystate.h @@ -211,10 +211,6 @@ struct _ts { * if it is NULL. */ PyAPI_FUNC(PyThreadState *) _PyThreadState_UncheckedGet(void); -PyAPI_FUNC(int) PyInterpreterState_SetRunningMain(PyInterpreterState *); -PyAPI_FUNC(void) PyInterpreterState_SetNotRunningMain(PyInterpreterState *); -PyAPI_FUNC(int) PyInterpreterState_IsRunningMain(PyInterpreterState *); - // Disable tracing and profiling. PyAPI_FUNC(void) PyThreadState_EnterTracing(PyThreadState *tstate); diff --git a/Include/internal/pycore_pystate.h b/Include/internal/pycore_pystate.h index 2e568f8aeeb152..6a36dba3708e38 100644 --- a/Include/internal/pycore_pystate.h +++ b/Include/internal/pycore_pystate.h @@ -44,6 +44,11 @@ _Py_IsMainInterpreterFinalizing(PyInterpreterState *interp) interp == &_PyRuntime._main_interpreter); } +// Export for _xxsubinterpreters module. +PyAPI_FUNC(int) _PyInterpreterState_SetRunningMain(PyInterpreterState *); +PyAPI_FUNC(void) _PyInterpreterState_SetNotRunningMain(PyInterpreterState *); +PyAPI_FUNC(int) _PyInterpreterState_IsRunningMain(PyInterpreterState *); + static inline const PyConfig * _Py_GetMainConfig(void) diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c index c09dbc3c72e712..e1c7d4ab2fd78f 100644 --- a/Modules/_xxsubinterpretersmodule.c +++ b/Modules/_xxsubinterpretersmodule.c @@ -8,6 +8,7 @@ #include "Python.h" #include "pycore_initconfig.h" // _PyErr_SetFromPyStatus() #include "pycore_pyerrors.h" // _PyErr_ChainExceptions1() +#include "pycore_pystate.h" // _PyInterpreterState_SetRunningMain() #include "interpreteridobject.h" @@ -361,7 +362,7 @@ static int _run_script(PyInterpreterState *interp, const char *codestr, _sharedns *shared, _sharedexception *sharedexc) { - if (PyInterpreterState_SetRunningMain(interp) < 0) { + if (_PyInterpreterState_SetRunningMain(interp) < 0) { // We skip going through the shared exception. return -1; } @@ -395,7 +396,7 @@ _run_script(PyInterpreterState *interp, const char *codestr, else { Py_DECREF(result); // We throw away the result. } - PyInterpreterState_SetNotRunningMain(interp); + _PyInterpreterState_SetNotRunningMain(interp); *sharedexc = no_exception; return 0; @@ -411,7 +412,7 @@ _run_script(PyInterpreterState *interp, const char *codestr, } Py_XDECREF(excval); assert(!PyErr_Occurred()); - PyInterpreterState_SetNotRunningMain(interp); + _PyInterpreterState_SetNotRunningMain(interp); return -1; } @@ -441,7 +442,7 @@ _run_script_in_interpreter(PyObject *mod, PyInterpreterState *interp, // exception gets raised in the right one. // XXX gh-109860: Drop this redundant check once we stop // re-using tstates that might already be in use. - if (PyInterpreterState_IsRunningMain(interp)) { + if (_PyInterpreterState_IsRunningMain(interp)) { PyErr_SetString(PyExc_RuntimeError, "interpreter already running"); if (shared != NULL) { @@ -566,7 +567,7 @@ interp_destroy(PyObject *self, PyObject *args, PyObject *kwds) // Ensure the interpreter isn't running. /* XXX We *could* support destroying a running interpreter but aren't going to worry about it for now. */ - if (PyInterpreterState_IsRunningMain(interp)) { + if (_PyInterpreterState_IsRunningMain(interp)) { PyErr_Format(PyExc_RuntimeError, "interpreter running"); return NULL; } @@ -748,7 +749,7 @@ interp_is_running(PyObject *self, PyObject *args, PyObject *kwds) if (interp == NULL) { return NULL; } - if (PyInterpreterState_IsRunningMain(interp)) { + if (_PyInterpreterState_IsRunningMain(interp)) { Py_RETURN_TRUE; } Py_RETURN_FALSE; diff --git a/Modules/main.c b/Modules/main.c index 9d18c923649d61..207ce7e8b0dadf 100644 --- a/Modules/main.c +++ b/Modules/main.c @@ -594,7 +594,7 @@ pymain_run_python(int *exitcode) pymain_header(config); - PyInterpreterState_SetRunningMain(interp); + _PyInterpreterState_SetRunningMain(interp); assert(!PyErr_Occurred()); if (config->run_command) { @@ -620,7 +620,7 @@ pymain_run_python(int *exitcode) *exitcode = pymain_exit_err_print(); done: - PyInterpreterState_SetNotRunningMain(interp); + _PyInterpreterState_SetNotRunningMain(interp); Py_XDECREF(main_importer_path); } diff --git a/Python/pystate.c b/Python/pystate.c index a961ca80bfd375..fe056bf4687026 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -1092,7 +1092,7 @@ _PyInterpreterState_DeleteExceptMain(_PyRuntimeState *runtime) int -PyInterpreterState_SetRunningMain(PyInterpreterState *interp) +_PyInterpreterState_SetRunningMain(PyInterpreterState *interp) { if (interp->threads.main != NULL) { PyErr_SetString(PyExc_RuntimeError, @@ -1111,14 +1111,14 @@ PyInterpreterState_SetRunningMain(PyInterpreterState *interp) } void -PyInterpreterState_SetNotRunningMain(PyInterpreterState *interp) +_PyInterpreterState_SetNotRunningMain(PyInterpreterState *interp) { assert(interp->threads.main == current_fast_get(&_PyRuntime)); interp->threads.main = NULL; } int -PyInterpreterState_IsRunningMain(PyInterpreterState *interp) +_PyInterpreterState_IsRunningMain(PyInterpreterState *interp) { return (interp->threads.main != NULL); }