Skip to content

Commit 7aa0f5d

Browse files
ericsnowcurrentlymrahtz
authored andcommitted
pythongh-113433: Automatically Clean Up Subinterpreters in Py_Finalize() (pythongh-121060)
This change makes things a little less painful for some users. It also fixes a failing assert (pythongh-120765), by making sure all subinterpreters are destroyed before the main interpreter. As part of that, we make sure Py_Finalize() always runs with the main interpreter active.
1 parent 1c991d4 commit 7aa0f5d

File tree

3 files changed

+151
-9
lines changed

3 files changed

+151
-9
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Subinterpreters now get cleaned up automatically during runtime
2+
finalization.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
:c:func:`Py_Finalize()` and :c:func:`Py_FinalizeEx()` now always run with
2+
the main interpreter active.

Python/pylifecycle.c

Lines changed: 147 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ static PyStatus init_sys_streams(PyThreadState *tstate);
7373
static PyStatus init_android_streams(PyThreadState *tstate);
7474
#endif
7575
static void wait_for_thread_shutdown(PyThreadState *tstate);
76+
static void finalize_subinterpreters(void);
7677
static void call_ll_exitfuncs(_PyRuntimeState *runtime);
7778

7879
/* The following places the `_PyRuntime` structure in a location that can be
@@ -1907,20 +1908,73 @@ finalize_interp_delete(PyInterpreterState *interp)
19071908
}
19081909

19091910

1910-
int
1911-
Py_FinalizeEx(void)
1911+
/* Conceptually, there isn't a good reason for Py_Finalize()
1912+
to be called in any other thread than the one where Py_Initialize()
1913+
was called. Consequently, it would make sense to fail if the thread
1914+
or thread state (or interpreter) don't match. However, such
1915+
constraints have never been enforced, and, as unlikely as it may be,
1916+
there may be users relying on the unconstrained behavior. Thus,
1917+
we do our best here to accommodate that possibility. */
1918+
1919+
static PyThreadState *
1920+
resolve_final_tstate(_PyRuntimeState *runtime)
1921+
{
1922+
PyThreadState *main_tstate = runtime->main_tstate;
1923+
assert(main_tstate != NULL);
1924+
assert(main_tstate->thread_id == runtime->main_thread);
1925+
PyInterpreterState *main_interp = _PyInterpreterState_Main();
1926+
assert(main_tstate->interp == main_interp);
1927+
1928+
PyThreadState *tstate = _PyThreadState_GET();
1929+
if (_Py_IsMainThread()) {
1930+
if (tstate != main_tstate) {
1931+
/* This implies that Py_Finalize() was called while
1932+
a non-main interpreter was active or while the main
1933+
tstate was temporarily swapped out with another.
1934+
Neither case should be allowed, but, until we get around
1935+
to fixing that (and Py_Exit()), we're letting it go. */
1936+
(void)PyThreadState_Swap(main_tstate);
1937+
}
1938+
}
1939+
else {
1940+
/* This is another unfortunate case where Py_Finalize() was
1941+
called when it shouldn't have been. We can't simply switch
1942+
over to the main thread. At the least, however, we can make
1943+
sure the main interpreter is active. */
1944+
if (!_Py_IsMainInterpreter(tstate->interp)) {
1945+
/* We don't go to the trouble of updating runtime->main_tstate
1946+
since it will be dead soon anyway. */
1947+
main_tstate =
1948+
_PyThreadState_New(main_interp, _PyThreadState_WHENCE_FINI);
1949+
if (main_tstate != NULL) {
1950+
_PyThreadState_Bind(main_tstate);
1951+
(void)PyThreadState_Swap(main_tstate);
1952+
}
1953+
else {
1954+
/* Fall back to the current tstate. It's better than nothing. */
1955+
main_tstate = tstate;
1956+
}
1957+
}
1958+
}
1959+
assert(main_tstate != NULL);
1960+
1961+
/* We might want to warn if main_tstate->current_frame != NULL. */
1962+
1963+
return main_tstate;
1964+
}
1965+
1966+
static int
1967+
_Py_Finalize(_PyRuntimeState *runtime)
19121968
{
19131969
int status = 0;
19141970

1915-
_PyRuntimeState *runtime = &_PyRuntime;
1971+
/* Bail out early if already finalized (or never initialized). */
19161972
if (!runtime->initialized) {
19171973
return status;
19181974
}
19191975

1920-
/* Get current thread state and interpreter pointer */
1921-
PyThreadState *tstate = _PyThreadState_GET();
1922-
// XXX assert(_Py_IsMainInterpreter(tstate->interp));
1923-
// XXX assert(_Py_IsMainThread());
1976+
/* Get final thread state pointer. */
1977+
PyThreadState *tstate = resolve_final_tstate(runtime);
19241978

19251979
// Block some operations.
19261980
tstate->interp->finalizing = 1;
@@ -1943,6 +1997,8 @@ Py_FinalizeEx(void)
19431997

19441998
_PyAtExit_Call(tstate->interp);
19451999

2000+
assert(_PyThreadState_GET() == tstate);
2001+
19462002
/* Copy the core config, PyInterpreterState_Delete() free
19472003
the core config memory */
19482004
#ifdef Py_REF_DEBUG
@@ -2023,6 +2079,9 @@ Py_FinalizeEx(void)
20232079
_PyImport_FiniExternal(tstate->interp);
20242080
finalize_modules(tstate);
20252081

2082+
/* Clean up any lingering subinterpreters. */
2083+
finalize_subinterpreters();
2084+
20262085
/* Print debug stats if any */
20272086
_PyEval_Fini();
20282087

@@ -2140,10 +2199,16 @@ Py_FinalizeEx(void)
21402199
return status;
21412200
}
21422201

2202+
int
2203+
Py_FinalizeEx(void)
2204+
{
2205+
return _Py_Finalize(&_PyRuntime);
2206+
}
2207+
21432208
void
21442209
Py_Finalize(void)
21452210
{
2146-
Py_FinalizeEx();
2211+
(void)_Py_Finalize(&_PyRuntime);
21472212
}
21482213

21492214

@@ -2355,6 +2420,79 @@ _Py_IsInterpreterFinalizing(PyInterpreterState *interp)
23552420
return finalizing != NULL;
23562421
}
23572422

2423+
static void
2424+
finalize_subinterpreters(void)
2425+
{
2426+
PyThreadState *final_tstate = _PyThreadState_GET();
2427+
PyInterpreterState *main_interp = _PyInterpreterState_Main();
2428+
assert(final_tstate->interp == main_interp);
2429+
_PyRuntimeState *runtime = main_interp->runtime;
2430+
struct pyinterpreters *interpreters = &runtime->interpreters;
2431+
2432+
/* Get the first interpreter in the list. */
2433+
HEAD_LOCK(runtime);
2434+
PyInterpreterState *interp = interpreters->head;
2435+
if (interp == main_interp) {
2436+
interp = interp->next;
2437+
}
2438+
HEAD_UNLOCK(runtime);
2439+
2440+
/* Bail out if there are no subinterpreters left. */
2441+
if (interp == NULL) {
2442+
return;
2443+
}
2444+
2445+
/* Warn the user if they forgot to clean up subinterpreters. */
2446+
(void)PyErr_WarnEx(
2447+
PyExc_RuntimeWarning,
2448+
"remaining subinterpreters; "
2449+
"destroy them with _interpreters.destroy()",
2450+
0);
2451+
2452+
/* Swap out the current tstate, which we know must belong
2453+
to the main interpreter. */
2454+
_PyThreadState_Detach(final_tstate);
2455+
2456+
/* Clean up all remaining subinterpreters. */
2457+
while (interp != NULL) {
2458+
assert(!_PyInterpreterState_IsRunningMain(interp));
2459+
2460+
/* Find the tstate to use for fini. We assume the interpreter
2461+
will have at most one tstate at this point. */
2462+
PyThreadState *tstate = interp->threads.head;
2463+
if (tstate != NULL) {
2464+
/* Ideally we would be able to use tstate as-is, and rely
2465+
on it being in a ready state: no exception set, not
2466+
running anything (tstate->current_frame), matching the
2467+
current thread ID (tstate->thread_id). To play it safe,
2468+
we always delete it and use a fresh tstate instead. */
2469+
assert(tstate != final_tstate);
2470+
_PyThreadState_Attach(tstate);
2471+
PyThreadState_Clear(tstate);
2472+
_PyThreadState_Detach(tstate);
2473+
PyThreadState_Delete(tstate);
2474+
}
2475+
tstate = _PyThreadState_NewBound(interp, _PyThreadState_WHENCE_FINI);
2476+
2477+
/* Destroy the subinterpreter. */
2478+
_PyThreadState_Attach(tstate);
2479+
Py_EndInterpreter(tstate);
2480+
assert(_PyThreadState_GET() == NULL);
2481+
2482+
/* Advance to the next interpreter. */
2483+
HEAD_LOCK(runtime);
2484+
interp = interpreters->head;
2485+
if (interp == main_interp) {
2486+
interp = interp->next;
2487+
}
2488+
HEAD_UNLOCK(runtime);
2489+
}
2490+
2491+
/* Switch back to the main interpreter. */
2492+
_PyThreadState_Attach(final_tstate);
2493+
}
2494+
2495+
23582496
/* Add the __main__ module */
23592497

23602498
static PyStatus
@@ -3216,7 +3354,7 @@ Py_Exit(int sts)
32163354
if (tstate != NULL && _PyThreadState_IsRunningMain(tstate)) {
32173355
_PyInterpreterState_SetNotRunningMain(tstate->interp);
32183356
}
3219-
if (Py_FinalizeEx() < 0) {
3357+
if (_Py_Finalize(&_PyRuntime) < 0) {
32203358
sts = 120;
32213359
}
32223360

0 commit comments

Comments
 (0)