René's URL Explorer Experiment


Title: PyThreadState_Swap() During Finalization Causes Immediate Exit (AKA Daemon Threads Are Still the Worst!) · Issue #109793 · python/cpython · GitHub

Open Graph Title: PyThreadState_Swap() During Finalization Causes Immediate Exit (AKA Daemon Threads Are Still the Worst!) · Issue #109793 · python/cpython

X Title: PyThreadState_Swap() During Finalization Causes Immediate Exit (AKA Daemon Threads Are Still the Worst!) · Issue #109793 · python/cpython

Description: Bug report tl;dr Switching between interpreters while finalizing causes the main thread to exit. The fix should be simple. We use PyThreadState_Swap() to switch between interpreters. That function almost immediately calls _PyEval_Acquire...

Open Graph Description: Bug report tl;dr Switching between interpreters while finalizing causes the main thread to exit. The fix should be simple. We use PyThreadState_Swap() to switch between interpreters. That function ...

X Description: Bug report tl;dr Switching between interpreters while finalizing causes the main thread to exit. The fix should be simple. We use PyThreadState_Swap() to switch between interpreters. That function ...

Opengraph URL: https://github.com/python/cpython/issues/109793

X: @github

direct link

Domain: github.com


Hey, it has json ld scripts:
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"PyThreadState_Swap() During Finalization Causes Immediate Exit (AKA Daemon Threads Are Still the Worst!)","articleBody":"# Bug report\r\n\r\ntl;dr Switching between interpreters while finalizing causes the main thread to exit.  The fix should be simple.\r\n\r\nWe use `PyThreadState_Swap()` to switch between interpreters.  That function almost immediately calls `_PyEval_AcquireLock()`.  During finalization, `_PyEval_AcquireLock()` immediately causes the thread to exit if the current thread state doesn't match the one that was active when `Py_FinalizeEx()` was called.\r\n\r\nThus, if we switch interpreters during finalization then the thread will exit.  If we do this in the finalizing (main) thread then the process immediately exits with an exit code of 0.\r\n\r\nOne notable consequence is that a Python process with an unhandled exception will print the traceback like normal but can end up with an exit code of 0 instead of 1 (and some of the runtime finalization code never gets executed). [^1]\r\n\r\n[^1]: This may help explain why, when we re-run some tests in subprocesses, they aren't marked as failures even when they actually fail.\r\n\r\n\r\n## Reproducer\r\n\r\n```shell\r\n$ cat \u003e script.py \u003c\u003c EOF\r\nimport _xxsubinterpreters as _interpreters\r\ninterpid = _interpreters.create()\r\nraise Exception\r\nEOF\r\n$ ./python script.py\r\nTraceback (most recent call last):\r\n  File \".../check-swapped-exitcode.py\", line 3, in \u003cmodule\u003e\r\n    raise Exception\r\nException\r\n$ echo $?\r\n0\r\n```\r\n\r\nIn this case, \"interpid\" is a `PyInterpreterIDObject` bound to the `__main__` module (of the main interpreter).  It is still bound there when the script ends and the executable starts finalizing the runtime by calling `Py_FinalizeEx()`. [^2]\r\n\r\n[^2]: Note that we did not create any extra threads; we stayed exclusively in the main thread.  We also didn't even run any code in the subinterpreter.\r\n\r\nHere's what happens in `Py_FinalizeEx()`:\r\n\r\n1. wait for non-daemon threads to finish [^3]\r\n2. run any remaining pending calls belong to the main interpreter\r\n3. run at exit hooks\r\n4. mark the runtime as finalizing (storing the pointer to the current tstate, which belongs to the main interpreter)\r\n5. delete all other tstates belong to the main interpreter (i.e. all daemon threads)\r\n6. remove our custom signal handlers\r\n7. finalize the import state\r\n9. clean up `sys.modules` of the main interpreter (`finalize_modules()` in Python/pylifecycle.c)\r\n\r\n[^3]: FYI, IIRC we used to abort right before this point if there were any subinterpreters around still.\r\n\r\nAt the point the following happens:\r\n\r\n1. the `__main__` module is dealloc'ed\r\n2. \"interpid\" is dealloc'ed (`PyInterpreterID_Type.tp_dealloc`)\r\n3. `_PyInterpreterState_IDDecref()` is called, which finalizes the corresponding interpreter state\r\n4, before `Py_EndInterpreter()` is called, we call `_PyThreadState_Swap()` to switch to a tstate belonging to the subinterpreter\r\n5. that calls `_PyEval_AcquireLock()`\r\n6. that basically calls `_PyThreadState_MustExit()`, which sees that the current tstate pointer isn't the one we stored as \"finalizing\"\r\n7. it then calls `PyThread_exit_thread()`, which kills the main thread\r\n8. the process exits with an exitcode of 0\r\n\r\nNotably, the rest of `Py_FinalizeEx()` (and `Py_Main()`, etc.) does *not* execute.  `main()` never gets a chance to return an exitcode of 1.\r\n\r\n\r\n## Background\r\n\r\nRuntime finalization happens in whichever thread called `Py_FinalizeEx()` and happens relative to whichever `PyThreadState` is active there.  This is typically the main thread and the main interpreter.\r\n\r\nOther threads may still be running when we start finalization, whether daemon threads or not, and each of those threads has a thread state corresponding to the interpreter that is active in that thread. [^4]  One of the first things we do during finalization is to wait for all non-daemon threads to finish running.  Daemon threads are a different story.  They must die!\r\n\r\n[^4]: In any given OS thread, each interpreter has a distinct tstate.  Each tstate (mostly) corresponds to exactly one OS thread.\r\n\r\nBack in 2011 we identified that daemon threads were interfering with finalization, sometimes causing crashes or making the Python executable hang.  [^5]  At the time, we applied a best-effort solution where we kill the current thread if it isn't the one where `Py_FinalizeEx()` was called.\r\n\r\n[^5]: If a daemon thread keeps running and tries to access any objects or other runtime state then there's a decent chance of a crash.\r\n\r\nHowever, that solution checked the tstate pointer rather than the thread ID, so swapping interpreters in the finalizing thread was broken, and here we are.\r\n\r\nHistory:\r\n\r\n* gh-46164 (2011; commit 0d5e52d3469) - exit thread during finalization in `PyEval_RestoreThread()` (also add `_Py_Finalizing`)\r\n* gh-???  (2014; commit 17548dda51d) - do same in `_PyEval_EvalFrameDefault()` (eval loop, right after re-acquiring GIL when handling eval breaker)\r\n* gh-80656 (2019; PR: gh-12667) - do same in `PyEval_AcquireLock()` and `PyEval_AcquireThread()` (also add `exit_thread_if_finalizing()`)\r\n* gh-84058 (2020; PR: gh-18811) - use `_PyRuntime` directly\r\n* gh-84058 (2020; PR: gh-18885) - move all the checks to `take_gil()`\r\n\r\nRelated:  gh-87135 (PRs: gh-105805, gh-28525)\n\n\u003c!-- gh-linked-prs --\u003e\n### Linked PRs\n* gh-109794\n* gh-110705\n\u003c!-- /gh-linked-prs --\u003e\n","author":{"url":"https://github.com/ericsnowcurrently","@type":"Person","name":"ericsnowcurrently"},"datePublished":"2023-09-23T19:39:05.000Z","interactionStatistic":{"@type":"InteractionCounter","interactionType":"https://schema.org/CommentAction","userInteractionCount":1},"url":"https://github.com/109793/cpython/issues/109793"}

route-pattern/_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format)
route-controllervoltron_issues_fragments
route-actionissue_layout
fetch-noncev2:26ee42f6-f28f-575c-d33a-0c18e9734ca1
current-catalog-service-hash81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114
request-id816C:36419E:1C65C85:24F9ACD:696B3AFD
html-safe-nonce2bf51d77a3386e0409890fb54469c384fb2b1cb860ac7001821a011a062c864a
visitor-payloadeyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiI4MTZDOjM2NDE5RToxQzY1Qzg1OjI0RjlBQ0Q6Njk2QjNBRkQiLCJ2aXNpdG9yX2lkIjoiNTcyNDYyOTEwNTkyMzI3NTUxNyIsInJlZ2lvbl9lZGdlIjoiaWFkIiwicmVnaW9uX3JlbmRlciI6ImlhZCJ9
visitor-hmac0828e18aff241df8906734fa08ec45846eaa18b71a59c2f6293a7cc2b7dbf4fc
hovercard-subject-tagissue:1909979080
github-keyboard-shortcutsrepository,issues,copilot
google-site-verificationApib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I
octolytics-urlhttps://collector.github.com/github/collect
analytics-location///voltron/issues_fragments/issue_layout
fb:app_id1401488693436528
apple-itunes-appapp-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/python/cpython/109793/issue_layout
twitter:imagehttps://opengraph.githubassets.com/f5c32fd944be460b71d647915a538c2856d13e620c7c5a2311aa80998018d83b/python/cpython/issues/109793
twitter:cardsummary_large_image
og:imagehttps://opengraph.githubassets.com/f5c32fd944be460b71d647915a538c2856d13e620c7c5a2311aa80998018d83b/python/cpython/issues/109793
og:image:altBug report tl;dr Switching between interpreters while finalizing causes the main thread to exit. The fix should be simple. We use PyThreadState_Swap() to switch between interpreters. That function ...
og:image:width1200
og:image:height600
og:site_nameGitHub
og:typeobject
og:author:usernameericsnowcurrently
hostnamegithub.com
expected-hostnamegithub.com
None5f99f7c1d70f01da5b93e5ca90303359738944d8ab470e396496262c66e60b8d
turbo-cache-controlno-preview
go-importgithub.com/python/cpython git https://github.com/python/cpython.git
octolytics-dimension-user_id1525981
octolytics-dimension-user_loginpython
octolytics-dimension-repository_id81598961
octolytics-dimension-repository_nwopython/cpython
octolytics-dimension-repository_publictrue
octolytics-dimension-repository_is_forkfalse
octolytics-dimension-repository_network_root_id81598961
octolytics-dimension-repository_network_root_nwopython/cpython
turbo-body-classeslogged-out env-production page-responsive
disable-turbofalse
browser-stats-urlhttps://api.github.com/_private/browser/stats
browser-errors-urlhttps://api.github.com/_private/browser/errors
release82560a55c6b2054555076f46e683151ee28a19bc
ui-targetfull
theme-color#1e2327
color-schemelight dark

Links:

Skip to contenthttps://github.com/python/cpython/issues/109793#start-of-content
https://github.com/
Sign in https://github.com/login?return_to=https%3A%2F%2Fgithub.com%2Fpython%2Fcpython%2Fissues%2F109793
GitHub CopilotWrite better code with AIhttps://github.com/features/copilot
GitHub SparkBuild and deploy intelligent appshttps://github.com/features/spark
GitHub ModelsManage and compare promptshttps://github.com/features/models
MCP RegistryNewIntegrate external toolshttps://github.com/mcp
ActionsAutomate any workflowhttps://github.com/features/actions
CodespacesInstant dev environmentshttps://github.com/features/codespaces
IssuesPlan and track workhttps://github.com/features/issues
Code ReviewManage code changeshttps://github.com/features/code-review
GitHub Advanced SecurityFind and fix vulnerabilitieshttps://github.com/security/advanced-security
Code securitySecure your code as you buildhttps://github.com/security/advanced-security/code-security
Secret protectionStop leaks before they starthttps://github.com/security/advanced-security/secret-protection
Why GitHubhttps://github.com/why-github
Documentationhttps://docs.github.com
Bloghttps://github.blog
Changeloghttps://github.blog/changelog
Marketplacehttps://github.com/marketplace
View all featureshttps://github.com/features
Enterpriseshttps://github.com/enterprise
Small and medium teamshttps://github.com/team
Startupshttps://github.com/enterprise/startups
Nonprofitshttps://github.com/solutions/industry/nonprofits
App Modernizationhttps://github.com/solutions/use-case/app-modernization
DevSecOpshttps://github.com/solutions/use-case/devsecops
DevOpshttps://github.com/solutions/use-case/devops
CI/CDhttps://github.com/solutions/use-case/ci-cd
View all use caseshttps://github.com/solutions/use-case
Healthcarehttps://github.com/solutions/industry/healthcare
Financial serviceshttps://github.com/solutions/industry/financial-services
Manufacturinghttps://github.com/solutions/industry/manufacturing
Governmenthttps://github.com/solutions/industry/government
View all industrieshttps://github.com/solutions/industry
View all solutionshttps://github.com/solutions
AIhttps://github.com/resources/articles?topic=ai
Software Developmenthttps://github.com/resources/articles?topic=software-development
DevOpshttps://github.com/resources/articles?topic=devops
Securityhttps://github.com/resources/articles?topic=security
View all topicshttps://github.com/resources/articles
Customer storieshttps://github.com/customer-stories
Events & webinarshttps://github.com/resources/events
Ebooks & reportshttps://github.com/resources/whitepapers
Business insightshttps://github.com/solutions/executive-insights
GitHub Skillshttps://skills.github.com
Documentationhttps://docs.github.com
Customer supporthttps://support.github.com
Community forumhttps://github.com/orgs/community/discussions
Trust centerhttps://github.com/trust-center
Partnershttps://github.com/partners
GitHub SponsorsFund open source developershttps://github.com/sponsors
Security Labhttps://securitylab.github.com
Maintainer Communityhttps://maintainers.github.com
Acceleratorhttps://github.com/accelerator
Archive Programhttps://archiveprogram.github.com
Topicshttps://github.com/topics
Trendinghttps://github.com/trending
Collectionshttps://github.com/collections
Enterprise platformAI-powered developer platformhttps://github.com/enterprise
GitHub Advanced SecurityEnterprise-grade security featureshttps://github.com/security/advanced-security
Copilot for BusinessEnterprise-grade AI featureshttps://github.com/features/copilot/copilot-business
Premium SupportEnterprise-grade 24/7 supporthttps://github.com/premium-support
Pricinghttps://github.com/pricing
Search syntax tipshttps://docs.github.com/search-github/github-code-search/understanding-github-code-search-syntax
documentationhttps://docs.github.com/search-github/github-code-search/understanding-github-code-search-syntax
Sign in https://github.com/login?return_to=https%3A%2F%2Fgithub.com%2Fpython%2Fcpython%2Fissues%2F109793
Sign up https://github.com/signup?ref_cta=Sign+up&ref_loc=header+logged+out&ref_page=%2F%3Cuser-name%3E%2F%3Crepo-name%3E%2Fvoltron%2Fissues_fragments%2Fissue_layout&source=header-repo&source_repo=python%2Fcpython
Reloadhttps://github.com/python/cpython/issues/109793
Reloadhttps://github.com/python/cpython/issues/109793
Reloadhttps://github.com/python/cpython/issues/109793
python https://github.com/python
cpythonhttps://github.com/python/cpython
Please reload this pagehttps://github.com/python/cpython/issues/109793
Notifications https://github.com/login?return_to=%2Fpython%2Fcpython
Fork 33.9k https://github.com/login?return_to=%2Fpython%2Fcpython
Star 71.1k https://github.com/login?return_to=%2Fpython%2Fcpython
Code https://github.com/python/cpython
Issues 5k+ https://github.com/python/cpython/issues
Pull requests 2.1k https://github.com/python/cpython/pulls
Actions https://github.com/python/cpython/actions
Projects 31 https://github.com/python/cpython/projects
Security Uh oh! There was an error while loading. Please reload this page. https://github.com/python/cpython/security
Please reload this pagehttps://github.com/python/cpython/issues/109793
Insights https://github.com/python/cpython/pulse
Code https://github.com/python/cpython
Issues https://github.com/python/cpython/issues
Pull requests https://github.com/python/cpython/pulls
Actions https://github.com/python/cpython/actions
Projects https://github.com/python/cpython/projects
Security https://github.com/python/cpython/security
Insights https://github.com/python/cpython/pulse
New issuehttps://github.com/login?return_to=https://github.com/python/cpython/issues/109793
New issuehttps://github.com/login?return_to=https://github.com/python/cpython/issues/109793
PyThreadState_Swap() During Finalization Causes Immediate Exit (AKA Daemon Threads Are Still the Worst!)https://github.com/python/cpython/issues/109793#top
https://github.com/ericsnowcurrently
3.12only security fixeshttps://github.com/python/cpython/issues?q=state%3Aopen%20label%3A%223.12%22
3.13bugs and security fixeshttps://github.com/python/cpython/issues?q=state%3Aopen%20label%3A%223.13%22
interpreter-core(Objects, Python, Grammar, and Parser dirs)https://github.com/python/cpython/issues?q=state%3Aopen%20label%3A%22interpreter-core%22
topic-subinterpretershttps://github.com/python/cpython/issues?q=state%3Aopen%20label%3A%22topic-subinterpreters%22
type-bugAn unexpected behavior, bug, or errorhttps://github.com/python/cpython/issues?q=state%3Aopen%20label%3A%22type-bug%22
https://github.com/ericsnowcurrently
https://github.com/ericsnowcurrently
ericsnowcurrentlyhttps://github.com/ericsnowcurrently
on Sep 23, 2023https://github.com/python/cpython/issues/109793#issue-1909979080
1https://github.com/python/cpython/issues/109793#user-content-fn-1-1a1f70ca387313647b111fc21e616cdd
2https://github.com/python/cpython/issues/109793#user-content-fn-2-1a1f70ca387313647b111fc21e616cdd
3https://github.com/python/cpython/issues/109793#user-content-fn-3-1a1f70ca387313647b111fc21e616cdd
4https://github.com/python/cpython/issues/109793#user-content-fn-4-1a1f70ca387313647b111fc21e616cdd
5https://github.com/python/cpython/issues/109793#user-content-fn-5-1a1f70ca387313647b111fc21e616cdd
shutdown (exit) can hang or segfault with daemon threads running #46164https://github.com/python/cpython/issues/46164
0d5e52dhttps://github.com/python/cpython/commit/0d5e52d3469a310001afe50689f77ddba6d554d1
17548ddhttps://github.com/python/cpython/commit/17548dda51ddd2fdc7641d8808b8f458f7f339fe
PyEval_AcquireLock() and PyEval_AcquireThread() do not handle runtime finalization properly. #80656https://github.com/python/cpython/issues/80656
bpo-36475: Finalize PyEval_AcquireLock() and PyEval_AcquireThread() properly #12667https://github.com/python/cpython/pull/12667
Daemon thread is crashing in PyEval_RestoreThread() while the main thread is exiting the process #84058https://github.com/python/cpython/issues/84058
bpo-39877: Fix PyEval_RestoreThread() for daemon threads #18811https://github.com/python/cpython/pull/18811
Daemon thread is crashing in PyEval_RestoreThread() while the main thread is exiting the process #84058https://github.com/python/cpython/issues/84058
bpo-39877: Refactor take_gil() function #18885https://github.com/python/cpython/pull/18885
gh-87135https://github.com/python/cpython/issues/87135
gh-105805https://github.com/python/cpython/pull/105805
gh-28525https://github.com/python/cpython/pull/28525
gh-109793: Allow Switching Interpreters During Finalization #109794https://github.com/python/cpython/pull/109794
[3.12] gh-109793: Allow Switching Interpreters During Finalization (gh-109794) #110705https://github.com/python/cpython/pull/110705
https://github.com/python/cpython/issues/109793#user-content-fnref-1-1a1f70ca387313647b111fc21e616cdd
https://github.com/python/cpython/issues/109793#user-content-fnref-2-1a1f70ca387313647b111fc21e616cdd
https://github.com/python/cpython/issues/109793#user-content-fnref-3-1a1f70ca387313647b111fc21e616cdd
https://github.com/python/cpython/issues/109793#user-content-fnref-4-1a1f70ca387313647b111fc21e616cdd
https://github.com/python/cpython/issues/109793#user-content-fnref-5-1a1f70ca387313647b111fc21e616cdd
ericsnowcurrentlyhttps://github.com/ericsnowcurrently
3.12only security fixeshttps://github.com/python/cpython/issues?q=state%3Aopen%20label%3A%223.12%22
3.13bugs and security fixeshttps://github.com/python/cpython/issues?q=state%3Aopen%20label%3A%223.13%22
interpreter-core(Objects, Python, Grammar, and Parser dirs)https://github.com/python/cpython/issues?q=state%3Aopen%20label%3A%22interpreter-core%22
topic-subinterpretershttps://github.com/python/cpython/issues?q=state%3Aopen%20label%3A%22topic-subinterpreters%22
type-bugAn unexpected behavior, bug, or errorhttps://github.com/python/cpython/issues?q=state%3Aopen%20label%3A%22type-bug%22
Subinterpretershttps://github.com/orgs/python/projects/3
https://github.com
Termshttps://docs.github.com/site-policy/github-terms/github-terms-of-service
Privacyhttps://docs.github.com/site-policy/privacy-policies/github-privacy-statement
Securityhttps://github.com/security
Statushttps://www.githubstatus.com/
Communityhttps://github.community/
Docshttps://docs.github.com/
Contacthttps://support.github.com?tags=dotcom-footer

Viewport: width=device-width


URLs of crawlers that visited me.