Title: subprocess can block all Python threads when using `vfork()` until the child process `exec()` succeeds or fails. · Issue #104372 · python/cpython · GitHub
Open Graph Title: subprocess can block all Python threads when using `vfork()` until the child process `exec()` succeeds or fails. · Issue #104372 · python/cpython
X Title: subprocess can block all Python threads when using `vfork()` until the child process `exec()` succeeds or fails. · Issue #104372 · python/cpython
Description: Background When using the subprocess module on Linux, vfork() rather than fork() has been preferred when possible for a few Python releases as is generally offers MUCH higher performance, the larger the parent process, the more cpu time ...
Open Graph Description: Background When using the subprocess module on Linux, vfork() rather than fork() has been preferred when possible for a few Python releases as is generally offers MUCH higher performance, the large...
X Description: Background When using the subprocess module on Linux, vfork() rather than fork() has been preferred when possible for a few Python releases as is generally offers MUCH higher performance, the large...
Opengraph URL: https://github.com/python/cpython/issues/104372
X: @github
Domain: github.com
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"subprocess can block all Python threads when using `vfork()` until the child process `exec()` succeeds or fails.","articleBody":"# Background\r\n\r\nWhen using the `subprocess` module on Linux, `vfork()` rather than `fork()` has been preferred when possible for a few Python releases as is generally offers MUCH higher performance, the larger the parent process, the more cpu time it saves on page table copying.\r\n\r\n# Problem\r\n\r\n`vfork()` - by design - does not return control to parent process until after the child process has successfully performed an `exec()` system call or has died.\r\n\r\nPython `subprocess` uses `_posixsubprocess.fork_exec()` extension module implementation to do handle async-signal-safe fork/vfork+exec code path (as that cannot safely be implemented in Python). This function does not release the GIL. Thus the GIL is held across the `vfork()` call. Meaning the parent process does not resume execution until the exec has succeeded, or failed and returned an error which our child writes to it's `errpipe_write` fd before exiting.\r\n\r\nThis can result in **all Python threads hanging** (ie: the GIL is held by a blocked thread) when the child `exec` system call takes a long time. Such as the binary existing on a potentially slow or high latency network filesystem. (We witnessed this on a FUSE filesystem backed by remote (high latency) cloud storage with a large executable... but any slow or high latency filesystem containing an executable or anything interfering in the exec system call can cause the same problem).\r\n\r\n# Workarounds\r\n\r\nBuilding without `vfork` support in `_posixsubprocess` or otherwise passing an arg to subprocess API that happens to disable vfork because it is incompatible with the vfork-concept are workarounds. _(I won't recommend any of those because none of them are a good idea to pass when not needed for their primary purpose)_.\r\n\r\nAnother workaround viable in our specific case is to pre-read the executable before asking subprocess to launch it as that moves the high IO latency from the `exec` system call into a read without the GIL held beforehand as it pulls the executable into a fast-enough local storage cache. (That being useful at all depends on your particular filesystem latency implementation situation)\r\n\r\n# Potential Solutions\r\n\r\nIf we could simply release the GIL around the `vfork()` call that would seem to be ideal... It's not clear to me that we can actually do that, I'm working out what a PR would look like or what blocks us from doing so safely.\r\n\r\nOffering yet another keyword argument to subprocess APIs to disable the use of APIs that could block the entire process upon `exec()` such as `vfork` is viable - but doesn't feel great given our existing long list of subprocess args.\n\n\u003c!-- gh-linked-prs --\u003e\n### Linked PRs\n* gh-104515\n* gh-104518\n* gh-104697\n* gh-104782\n* gh-104785\n* gh-104831\n* gh-104833\n* gh-104942\n* gh-104958\n\u003c!-- /gh-linked-prs --\u003e\n","author":{"url":"https://github.com/gpshead","@type":"Person","name":"gpshead"},"datePublished":"2023-05-10T23:41:38.000Z","interactionStatistic":{"@type":"InteractionCounter","interactionType":"https://schema.org/CommentAction","userInteractionCount":3},"url":"https://github.com/104372/cpython/issues/104372"}
| route-pattern | /_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format) |
| route-controller | voltron_issues_fragments |
| route-action | issue_layout |
| fetch-nonce | v2:ab0de215-5d2e-2bb9-6283-b63075c5cd61 |
| current-catalog-service-hash | 81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114 |
| request-id | 9D30:DC558:A26E15:D7A012:69699223 |
| html-safe-nonce | acf94712e2ed885c028a0138d460c48d75b96d0aeb32b4e4289e4ed9b846e1dc |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiI5RDMwOkRDNTU4OkEyNkUxNTpEN0EwMTI6Njk2OTkyMjMiLCJ2aXNpdG9yX2lkIjoiMzg3Njc2NzgyNjgwNTYyNTM3OSIsInJlZ2lvbl9lZGdlIjoiaWFkIiwicmVnaW9uX3JlbmRlciI6ImlhZCJ9 |
| visitor-hmac | fe23de2a11cc4d070abdde89b56ae96f4522f8145435a23e1375340c9a805cc2 |
| hovercard-subject-tag | issue:1704756491 |
| github-keyboard-shortcuts | repository,issues,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/python/cpython/104372/issue_layout |
| twitter:image | https://opengraph.githubassets.com/3075ca9d07a55093881e20a97d702289e5002f2a1b32aece86fbb4c8b02055fe/python/cpython/issues/104372 |
| twitter:card | summary_large_image |
| og:image | https://opengraph.githubassets.com/3075ca9d07a55093881e20a97d702289e5002f2a1b32aece86fbb4c8b02055fe/python/cpython/issues/104372 |
| og:image:alt | Background When using the subprocess module on Linux, vfork() rather than fork() has been preferred when possible for a few Python releases as is generally offers MUCH higher performance, the large... |
| og:image:width | 1200 |
| og:image:height | 600 |
| og:site_name | GitHub |
| og:type | object |
| og:author:username | gpshead |
| hostname | github.com |
| expected-hostname | github.com |
| None | 3542e147982176a7ebaa23dfb559c8af16f721c03ec560c68c56b64a0f35e751 |
| turbo-cache-control | no-preview |
| go-import | github.com/python/cpython git https://github.com/python/cpython.git |
| octolytics-dimension-user_id | 1525981 |
| octolytics-dimension-user_login | python |
| octolytics-dimension-repository_id | 81598961 |
| octolytics-dimension-repository_nwo | python/cpython |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 81598961 |
| octolytics-dimension-repository_network_root_nwo | python/cpython |
| turbo-body-classes | logged-out env-production page-responsive |
| disable-turbo | false |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | af80af7cc9e3de9c336f18b208a600950a3c187c |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width