-
-
Notifications
You must be signed in to change notification settings - Fork 30.3k
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
gh-113964: Don't prevent new threads until all non-daemon threads exit #116677
Conversation
…ds exit Starting in Python 3.12, we started preventing fork() and starting new threads during interpreter finalization (shutdown). This has led to a number of regressions and flaky tests. We should not prevent starting new threads (or fork()) until all non-daemon threads exit and finalization starts in earnest. This changes the checks to use `_PyInterpreterState_GetFinalizing(interp)`, which is set immediately before terminating non-daemon threads.
Thanks for looking at this! I'll leave it up to @gpshead to review. |
History of (1) June 2023, issue gh-104690: (2) Sept 2023, issue gh-108987: // gh-108987: If _thread.start_new_thread() is called before or while
// Python is being finalized, thread_run() can called *after*.
// _PyRuntimeState_SetFinalizing() is called. At this point, all Python
// threads must exit, except of the thread calling Py_Finalize() whch holds
// the GIL and must not exit.
//
// At this stage, tstate can be a dangling pointer (point to freed memory),
// it's ok to call _PyThreadState_MustExit() with a dangling pointer.
if (_PyThreadState_MustExit(tstate)) {
// Don't call PyThreadState_Clear() nor _PyThreadState_DeleteCurrent().
// These functions are called on tstate indirectly by Py_Finalize()
// which calls _PyInterpreterState_Clear().
//
// Py_DECREF() cannot be called because the GIL is not held: leak
// references on purpose. Python is being finalized anyway.
thread_bootstate_free(boot, 0);
goto exit;
} (3) Feb 2024, gh-114570: I added With all these changes, I'm no longer sure what should be the correct behavior. I only hope that our test suite is now testing cases which caused crashes previously. I prefer to let someone else decide on this issue :-) The Python finalization is fragile. I took notes on old issues and the history of changes at: https://pythondev.readthedocs.io/finalization.html |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general I think this change is correct. I believe we'll want to backport this to 3.12 (I haven't looked at how difficult that'll be though) as the issue came up there as being a behavior regression from <=3.11 that reasonable code relies on.
One thing I think we're missing with the unit test transformations to be "AtFinalization" via the main interpreter exiting and triggering is: Alternate explicit tests for atexit.register()'d handlers at finalization time. Which implies we need to explicitly define what that behavior should be. But that may be a separate PR, i've noted that on the issue.
For this, see also #115219 which I may mark a dupe.
I created #116982 for atexit - please take a look at that. Keeping these separate makes sense, and yours should go in first. But it'll be an easier merge if the tests I build on are not removed if feasible. (otherwise no worries, i'll untangle it when merging into mine) |
@gpshead - I don't see a way to avoid removing/replacing the atexit tests. The existing atexit tests would not pass with this PR (because the exception is not raised), and the atexit tests from your PR would not pass reliably without the other changes from your PR. |
I will go ahead and merge this with apologies for the inevitable merge conflicts. |
Thanks @colesbury for the PR 🌮🎉.. I'm working now to backport this PR to: 3.12. |
Sorry, @colesbury, I could not cleanly backport this to
|
GH-117029 is a backport of this pull request to the 3.12 branch. |
…n threads exit (pythonGH-116677) Starting in Python 3.12, we prevented calling fork() and starting new threads during interpreter finalization (shutdown). This has led to a number of regressions and flaky tests. We should not prevent starting new threads (or `fork()`) until all non-daemon threads exit and finalization starts in earnest. This changes the checks to use `_PyInterpreterState_GetFinalizing(interp)`, which is set immediately before terminating non-daemon threads. (cherry picked from commit 60e105c) Co-authored-by: Sam Gross <[email protected]>
…ads exit (GH-116677) (#117029) Starting in Python 3.12, we prevented calling fork() and starting new threads during interpreter finalization (shutdown). This has led to a number of regressions and flaky tests. We should not prevent starting new threads (or `fork()`) until all non-daemon threads exit and finalization starts in earnest. This changes the checks to use `_PyInterpreterState_GetFinalizing(interp)`, which is set immediately before terminating non-daemon threads. (cherry picked from commit 60e105c)
…ds exit (python#116677) Starting in Python 3.12, we prevented calling fork() and starting new threads during interpreter finalization (shutdown). This has led to a number of regressions and flaky tests. We should not prevent starting new threads (or `fork()`) until all non-daemon threads exit and finalization starts in earnest. This changes the checks to use `_PyInterpreterState_GetFinalizing(interp)`, which is set immediately before terminating non-daemon threads.
…ds exit (python#116677) Starting in Python 3.12, we prevented calling fork() and starting new threads during interpreter finalization (shutdown). This has led to a number of regressions and flaky tests. We should not prevent starting new threads (or `fork()`) until all non-daemon threads exit and finalization starts in earnest. This changes the checks to use `_PyInterpreterState_GetFinalizing(interp)`, which is set immediately before terminating non-daemon threads.
…ds exit (python#116677) Starting in Python 3.12, we prevented calling fork() and starting new threads during interpreter finalization (shutdown). This has led to a number of regressions and flaky tests. We should not prevent starting new threads (or `fork()`) until all non-daemon threads exit and finalization starts in earnest. This changes the checks to use `_PyInterpreterState_GetFinalizing(interp)`, which is set immediately before terminating non-daemon threads.
Starting in Python 3.12, we started preventing fork() and starting new threads during interpreter finalization (shutdown). This has led to a number of regressions and flaky tests. We should not prevent starting new threads (or fork()) until all non-daemon threads exit and finalization starts in earnest.
This changes the checks to use
_PyInterpreterState_GetFinalizing(interp)
, which is set immediately before terminating non-daemon threads.