Title: asyncio: Use strong references for free-flying tasks · Issue #91887 · python/cpython · GitHub
Open Graph Title: asyncio: Use strong references for free-flying tasks · Issue #91887 · python/cpython
X Title: asyncio: Use strong references for free-flying tasks · Issue #91887 · python/cpython
Description: In #88831 @vincentbernat pointed out that CPython only keeps weak references in _all_tasks, so a reference to a Task returned by loop.create_task has to be kept to be sure the task will not be killed with a "Task was destroyed but it is ...
Open Graph Description: In #88831 @vincentbernat pointed out that CPython only keeps weak references in _all_tasks, so a reference to a Task returned by loop.create_task has to be kept to be sure the task will not be kill...
X Description: In #88831 @vincentbernat pointed out that CPython only keeps weak references in _all_tasks, so a reference to a Task returned by loop.create_task has to be kept to be sure the task will not be kill...
Opengraph URL: https://github.com/python/cpython/issues/91887
X: @github
Domain: github.com
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"asyncio: Use strong references for free-flying tasks","articleBody":"In #88831 @vincentbernat pointed out that CPython only keeps weak references in [`_all_tasks`](https://github.com/python/cpython/blob/090721721b373c50544d297b56c217cf15992cbe/Lib/asyncio/tasks.py#L933), so a reference to a `Task` returned by `loop.create_task` has to be kept to be sure the task will not be killed with a \"Task was destroyed but it is pending!\" at some random point in time.\r\n\r\nWhen shielding a task from cancellation with `await shield(something())`, `something` continues to run when the containing coroutine is cancelled. As soon as that happens, `something()` is free-flying, i.e. there's no reference from user code anymore. `shield` itself has a bunch of circular strong references, but these shouldn't keep CPython from garbage-collecting the task. Hence, here the same problem occurs and the task might be killed unpredictably. Additionally, when running coroutines in parallel with `gather` and `return_exceptions=False`, an exception in one of the coroutines will leave remaining tasks free-flying. Also in this case, the remaining tasks might be killed unpredictably.\r\n\r\nHence, a warning in the documentation for `create_task` unfortunately does not suffice to solve the problem. Additionally, it has been brought up in #88831 that an API for fire-and-forget tasks (i.e. when the user doesn't want to keep a reference) would be nice.\r\n\r\nAs solution, I suggest to either\r\n\r\n(1) introduce a further `_pending_tasks` set to keep strong references to all pending tasks. This would be the simplest solution also with respect to the API. In fact, a lot of dicussions on Stack Overflow (e.g., [here](https://stackoverflow.com/a/65921782), [here](https://stackoverflow.com/a/63425909), [here](https://stackoverflow.com/a/56356487)) already rely on this behavior (throwing away the reference returned by `create_task`), although it's wrong currently. Since the behaviour for free-flying tasks is unpredictable currently, it should not introduce any compatibility issues when making it predictable by preventing them from being garbage-collected.\r\n\r\n(2) make sure there's always a chain of strong references from the most basic futures to the running tasks awaiting something. A quick `grep` resulted in potential problems, e.g., [here](https://github.com/python/cpython/blob/090721721b373c50544d297b56c217cf15992cbe/Lib/asyncio/selector_events.py#L57) and [here](https://github.com/python/cpython/blob/090721721b373c50544d297b56c217cf15992cbe/Lib/asyncio/streams.py#L198). This does not seem like a very robust approach, though.\r\n\r\n(3) introduce the concept of _background tasks_, i.e., tasks the user does not want to hold references to. The interface could look like suggested in https://github.com/python/cpython/issues/88831#issuecomment-1105619239 . Tasks from `shield` and `gather` could be automatically converted to such background tasks. Clearly, it would add complexity to the API, but the distinction between normal tasks and background tasks might potentially be beneficial also for other purposes. E.g., one might add an API call that waits for all background tasks to be completed.\r\n\r\nMy preferred solution would be (1).\n\n\u003c!-- gh-linked-prs --\u003e\n### Linked PRs\n* gh-121264\n\u003c!-- /gh-linked-prs --\u003e\n","author":{"url":"https://github.com/alexhartl","@type":"Person","name":"alexhartl"},"datePublished":"2022-04-24T19:25:49.000Z","interactionStatistic":{"@type":"InteractionCounter","interactionType":"https://schema.org/CommentAction","userInteractionCount":52},"url":"https://github.com/91887/cpython/issues/91887"}
| 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:25b2e067-fa17-5848-9ce3-e7df906dde08 |
| current-catalog-service-hash | 81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114 |
| request-id | DC18:39E51F:A9EF25:DDEB89:696AF848 |
| html-safe-nonce | 42b26dca14e471f9c442f9a9bd4e7926825b476e807438d36de44be59e0cba39 |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJEQzE4OjM5RTUxRjpBOUVGMjU6RERFQjg5OjY5NkFGODQ4IiwidmlzaXRvcl9pZCI6IjE5MDYyNTM2OTM0NDI1MjExNjAiLCJyZWdpb25fZWRnZSI6ImlhZCIsInJlZ2lvbl9yZW5kZXIiOiJpYWQifQ== |
| visitor-hmac | 23a92ca29116c4933246d8f46d42288eb25078ae374b574aeb1d881848af3efa |
| hovercard-subject-tag | issue:1213745290 |
| 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/91887/issue_layout |
| twitter:image | https://opengraph.githubassets.com/540305c4c7691529225a7c4228add62ad59060fe9e10b29eb8cf0571fea84271/python/cpython/issues/91887 |
| twitter:card | summary_large_image |
| og:image | https://opengraph.githubassets.com/540305c4c7691529225a7c4228add62ad59060fe9e10b29eb8cf0571fea84271/python/cpython/issues/91887 |
| og:image:alt | In #88831 @vincentbernat pointed out that CPython only keeps weak references in _all_tasks, so a reference to a Task returned by loop.create_task has to be kept to be sure the task will not be kill... |
| og:image:width | 1200 |
| og:image:height | 600 |
| og:site_name | GitHub |
| og:type | object |
| og:author:username | alexhartl |
| hostname | github.com |
| expected-hostname | github.com |
| None | 5f99f7c1d70f01da5b93e5ca90303359738944d8ab470e396496262c66e60b8d |
| 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 | 82560a55c6b2054555076f46e683151ee28a19bc |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width