Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 34k
gh-128639: Don't assume one thread in subinterpreter finalization#128640
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
025cc17eea3ba9149e0cf1761c378febfbf5a73ce1a7cc3fe7c44c0b6551bfaf19e28d02da72aa75954076e4ec91555731d61099e09851dcb223396File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -132,6 +132,7 @@ def test_sys_path_0(self): | ||
| 'sub': sys.path[0], | ||
| }}, indent=4), flush=True) | ||
| """) | ||
| interp.close() | ||
ericsnowcurrently marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading. Please reload this page. | ||
| ''' | ||
| # <tmp>/ | ||
| # pkg/ | ||
| @@ -172,7 +173,10 @@ def test_gh_109793(self): | ||
| argv = [sys.executable, '-c', '''if True: | ||
| from test.support import interpreters | ||
| interp = interpreters.create() | ||
| raise Exception | ||
| try: | ||
| raise Exception | ||
| finally: | ||
| interp.close() | ||
| '''] | ||
| proc = subprocess.run(argv, capture_output=True, text=True) | ||
| self.assertIn('Traceback', proc.stderr) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| Fix a crash when using threads inside of a subinterpreter. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1424,9 +1424,12 @@ static int test_audit_subinterpreter(void) | ||
| PySys_AddAuditHook(_audit_subinterpreter_hook, NULL); | ||
| _testembed_Py_InitializeFromConfig(); | ||
| Py_NewInterpreter(); | ||
| Py_NewInterpreter(); | ||
| Py_NewInterpreter(); | ||
| PyThreadState *tstate = PyThreadState_Get(); | ||
MemberAuthor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this test working before was a fluke. Member There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's probably okay but your change helps future-proof the test. | ||
| for (int i = 0; i < 3; ++i) | ||
| { | ||
| Py_EndInterpreter(Py_NewInterpreter()); | ||
| PyThreadState_Swap(tstate); | ||
| } | ||
| Py_Finalize(); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1982,6 +1982,7 @@ resolve_final_tstate(_PyRuntimeState *runtime) | ||
| } | ||
| else{ | ||
| /* Fall back to the current tstate. It's better than nothing. */ | ||
| // XXX No it's not | ||
| main_tstate = tstate; | ||
| } | ||
| } | ||
| @@ -2027,6 +2028,16 @@ _Py_Finalize(_PyRuntimeState *runtime) | ||
| _PyAtExit_Call(tstate->interp); | ||
| /* Clean up any lingering subinterpreters. | ||
| Two preconditions need to be met here: | ||
| - This has to happen before _PyRuntimeState_SetFinalizing is | ||
| called, or else threads might get prematurely blocked. | ||
| - The world must not be stopped, as finalizers can run. | ||
| */ | ||
| finalize_subinterpreters(); | ||
| assert(_PyThreadState_GET() == tstate); | ||
| /* Copy the core config, PyInterpreterState_Delete() free | ||
| @@ -2114,9 +2125,6 @@ _Py_Finalize(_PyRuntimeState *runtime) | ||
| _PyImport_FiniExternal(tstate->interp); | ||
| finalize_modules(tstate); | ||
| /* Clean up any lingering subinterpreters. */ | ||
| finalize_subinterpreters(); | ||
| /* Print debug stats if any */ | ||
| _PyEval_Fini(); | ||
| @@ -2400,9 +2408,8 @@ Py_NewInterpreter(void) | ||
| return tstate; | ||
| } | ||
| /* Delete an interpreter and its last thread. This requires that the | ||
| given thread state is current, that the thread has no remaining | ||
| frames, and that it is its interpreter's only remaining thread. | ||
| /* Delete an interpreter. This requires that the given thread state | ||
| is current, and that the thread has no remaining frames. | ||
ericsnowcurrently marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading. Please reload this page. | ||
| It is a fatal error to violate these constraints. | ||
| (Py_FinalizeEx() doesn't have these constraints -- it zaps | ||
| @@ -2432,14 +2439,15 @@ Py_EndInterpreter(PyThreadState *tstate) | ||
| _Py_FinishPendingCalls(tstate); | ||
| _PyAtExit_Call(tstate->interp); | ||
| if (tstate != interp->threads.head || tstate->next != NULL){ | ||
| Py_FatalError("not the last thread"); | ||
| } | ||
| _PyRuntimeState *runtime = interp->runtime; | ||
| _PyEval_StopTheWorldAll(runtime); | ||
ericsnowcurrently marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading. Please reload this page. | ||
| PyThreadState *list = _PyThreadState_RemoveExcept(tstate); | ||
| /* Remaining daemon threads will automatically exit | ||
| when they attempt to take the GIL (ex: PyEval_RestoreThread()). */ | ||
| _PyInterpreterState_SetFinalizing(interp, tstate); | ||
| _PyEval_StartTheWorldAll(runtime); | ||
| _PyThreadState_DeleteList(list, /*is_after_fork=*/0); | ||
ericsnowcurrently marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading. Please reload this page. | ||
| // XXX Call something like _PyImport_Disable() here? | ||
| @@ -2470,6 +2478,8 @@ finalize_subinterpreters(void) | ||
| PyInterpreterState *main_interp = _PyInterpreterState_Main(); | ||
| assert(final_tstate->interp == main_interp); | ||
| _PyRuntimeState *runtime = main_interp->runtime; | ||
| assert(!runtime->stoptheworld.world_stopped); | ||
| assert(_PyRuntimeState_GetFinalizing(runtime) == NULL); | ||
| struct pyinterpreters *interpreters = &runtime->interpreters; | ||
| /* Get the first interpreter in the list. */ | ||
| @@ -2498,27 +2508,17 @@ finalize_subinterpreters(void) | ||
| /* Clean up all remaining subinterpreters. */ | ||
| while (interp != NULL){ | ||
| assert(!_PyInterpreterState_IsRunningMain(interp)); | ||
| /* Find the tstate to use for fini. We assume the interpreter | ||
| will have at most one tstate at this point. */ | ||
| PyThreadState *tstate = interp->threads.head; | ||
| if (tstate != NULL){ | ||
| /* Ideally we would be able to use tstate as-is, and rely | ||
| on it being in a ready state: no exception set, not | ||
| running anything (tstate->current_frame), matching the | ||
| current thread ID (tstate->thread_id). To play it safe, | ||
| we always delete it and use a fresh tstate instead. */ | ||
| assert(tstate != final_tstate); | ||
| _PyThreadState_Attach(tstate); | ||
| PyThreadState_Clear(tstate); | ||
| _PyThreadState_Detach(tstate); | ||
| PyThreadState_Delete(tstate); | ||
| /* Make a tstate for finalization. */ | ||
| PyThreadState *tstate = _PyThreadState_NewBound(interp, _PyThreadState_WHENCE_FINI); | ||
ericsnowcurrently marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading. Please reload this page. | ||
| if (tstate == NULL){ | ||
| // XXX Some graceful way to always get a thread state? | ||
| Py_FatalError("thread state allocation failed"); | ||
ZeroIntensity marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading. Please reload this page. | ||
| } | ||
| tstate = _PyThreadState_NewBound(interp, _PyThreadState_WHENCE_FINI); | ||
| /* Destroy the subinterpreter. */ | ||
| /* Enter the subinterpreter. */ | ||
| _PyThreadState_Attach(tstate); | ||
| /* Destroy the subinterpreter. */ | ||
| Py_EndInterpreter(tstate); | ||
| assert(_PyThreadState_GET() == NULL); | ||
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.