Skip to content

Commit b3d25df

Browse files
gh-105716: Fix _PyInterpreterState_IsRunningMain() For Embedders (gh-117140)
When I added _PyInterpreterState_IsRunningMain() and friends last year, I tried to accommodate applications that embed Python but don't call _PyInterpreterState_SetRunningMain() (not that they're expected to). That mostly worked fine until my recent changes in gh-117049, where the subtleties with the fallback code led to failures; the change ended up breaking test_tools.test_freeze, which exercises a basic embedding situation. The simplest fix is to drop the fallback code I originally added to _PyInterpreterState_IsRunningMain() (and later to _PyThreadState_IsRunningMain()). I've kept the fallback in the _xxsubinterpreters module though. I've also updated Py_FrozenMain() to call _PyInterpreterState_SetRunningMain().
1 parent c4bf58a commit b3d25df

File tree

3 files changed

+39
-22
lines changed

3 files changed

+39
-22
lines changed

Modules/_xxsubinterpretersmodule.c

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,24 @@ _get_current_module(void)
5656
}
5757

5858

59+
static int
60+
is_running_main(PyInterpreterState *interp)
61+
{
62+
if (_PyInterpreterState_IsRunningMain(interp)) {
63+
return 1;
64+
}
65+
// Unlike with the general C-API, we can be confident that someone
66+
// using this module for the main interpreter is doing so through
67+
// the main program. Thus we can make this extra check. This benefits
68+
// applications that embed Python but haven't been updated yet
69+
// to call_PyInterpreterState_SetRunningMain().
70+
if (_Py_IsMainInterpreter(interp)) {
71+
return 1;
72+
}
73+
return 0;
74+
}
75+
76+
5977
/* Cross-interpreter Buffer Views *******************************************/
6078

6179
// XXX Release when the original interpreter is destroyed.
@@ -509,7 +527,7 @@ interp_destroy(PyObject *self, PyObject *args, PyObject *kwds)
509527
// Ensure the interpreter isn't running.
510528
/* XXX We *could* support destroying a running interpreter but
511529
aren't going to worry about it for now. */
512-
if (_PyInterpreterState_IsRunningMain(interp)) {
530+
if (is_running_main(interp)) {
513531
PyErr_Format(PyExc_RuntimeError, "interpreter running");
514532
return NULL;
515533
}
@@ -977,7 +995,7 @@ interp_is_running(PyObject *self, PyObject *args, PyObject *kwds)
977995
if (interp == NULL) {
978996
return NULL;
979997
}
980-
if (_PyInterpreterState_IsRunningMain(interp)) {
998+
if (is_running_main(interp)) {
981999
Py_RETURN_TRUE;
9821000
}
9831001
Py_RETURN_FALSE;

Python/frozenmain.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,12 @@ Py_FrozenMain(int argc, char **argv)
5454
Py_ExitStatusException(status);
5555
}
5656

57+
PyInterpreterState *interp = PyInterpreterState_Get();
58+
if (_PyInterpreterState_SetRunningMain(interp) < 0) {
59+
PyErr_Print();
60+
exit(1);
61+
}
62+
5763
#ifdef MS_WINDOWS
5864
PyWinFreeze_ExeInit();
5965
#endif
@@ -83,6 +89,9 @@ Py_FrozenMain(int argc, char **argv)
8389
#ifdef MS_WINDOWS
8490
PyWinFreeze_ExeTerm();
8591
#endif
92+
93+
_PyInterpreterState_SetNotRunningMain(interp);
94+
8695
if (Py_FinalizeEx() < 0) {
8796
sts = 120;
8897
}

Python/pystate.c

Lines changed: 10 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1042,34 +1042,24 @@ _PyInterpreterState_IsRunningMain(PyInterpreterState *interp)
10421042
if (interp->threads.main != NULL) {
10431043
return 1;
10441044
}
1045-
// For now, we assume the main interpreter is always running.
1046-
if (_Py_IsMainInterpreter(interp)) {
1047-
return 1;
1048-
}
1045+
// Embedders might not know to call _PyInterpreterState_SetRunningMain(),
1046+
// so their main thread wouldn't show it is running the main interpreter's
1047+
// program. (Py_Main() doesn't have this problem.) For now this isn't
1048+
// critical. If it were, we would need to infer "running main" from other
1049+
// information, like if it's the main interpreter. We used to do that
1050+
// but the naive approach led to some inconsistencies that caused problems.
10491051
return 0;
10501052
}
10511053

1052-
#ifndef NDEBUG
1053-
static int
1054-
is_running_main(PyThreadState *tstate)
1055-
{
1056-
if (tstate->interp->threads.main != NULL) {
1057-
return tstate == tstate->interp->threads.main;
1058-
}
1059-
return 0;
1060-
}
1061-
#endif
1062-
10631054
int
10641055
_PyThreadState_IsRunningMain(PyThreadState *tstate)
10651056
{
10661057
PyInterpreterState *interp = tstate->interp;
10671058
if (interp->threads.main != NULL) {
10681059
return tstate == interp->threads.main;
10691060
}
1070-
if (_Py_IsMainInterpreter(interp)) {
1071-
return tstate->thread_id == interp->runtime->main_thread;
1072-
}
1061+
// See the note in _PyInterpreterState_IsRunningMain() about
1062+
// possible false negatives here for embedders.
10731063
return 0;
10741064
}
10751065

@@ -1571,7 +1561,7 @@ PyThreadState_Clear(PyThreadState *tstate)
15711561
{
15721562
assert(tstate->_status.initialized && !tstate->_status.cleared);
15731563
assert(current_fast_get()->interp == tstate->interp);
1574-
assert(!is_running_main(tstate));
1564+
assert(!_PyThreadState_IsRunningMain(tstate));
15751565
// XXX assert(!tstate->_status.bound || tstate->_status.unbound);
15761566
tstate->_status.finalizing = 1; // just in case
15771567

@@ -1670,7 +1660,7 @@ tstate_delete_common(PyThreadState *tstate)
16701660
assert(tstate->_status.cleared && !tstate->_status.finalized);
16711661
assert(tstate->state != _Py_THREAD_ATTACHED);
16721662
tstate_verify_not_active(tstate);
1673-
assert(!is_running_main(tstate));
1663+
assert(!_PyThreadState_IsRunningMain(tstate));
16741664

16751665
PyInterpreterState *interp = tstate->interp;
16761666
if (interp == NULL) {

0 commit comments

Comments
 (0)