Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork32k
Description
Bug report
Bug description:
As I understand it,asyncio.TaskGroup
should never silently lose a task. Any time you useTaskGroup.create_task()
, one of these two things happens:
- The coroutine you pass runs to the end, with the
TaskGroup
context manager waiting until this happens. Possibly the task is cancelled, maybe due to another task in the group raising an exception, but the coroutine "sees" this (it gets the cancellation exception) so it can always do some handling of it if needed, and the task group still waits for that all to finish. - The
TaskGroup.create_task()
method raises aRuntimeError
because it is not possible to start the task. This happens when the task group is not running (because the context manager hasn't been entered or has already exited), or because it is in the process of shutting down (because one of the tasks in it has finished with an exception).
(Disclaimer: My background is as a user of Trio, where these are definitely the only two possibilities. The main difference is that starting a task in an active but cancelled Trio nursery will start a task and cancel it immediately, which allows it to run to its first unshieldedawait
, rather than raising an exception fromstart_soon()
. But that's a small design difference. The point is that you are still guaranteed one of the two possibilities.)
However, a task can be silently lost if the task group it is in gets shut down before a recently-created task has a chance to get scheduled at all, as in this example:
asyncwithasyncio.TaskGroup()astg:tg.create_task(my_fn(3))raiseRuntimeError
This snippet seems a bit silly because the task group gets shut down by an exception from the same child task that is spawning a new sibling. But the same situation can happen when an uncaught exception gets thrown by one child at roughly the same time that another child has spawned a sibling. (I came across this issue by launching a task from an inner task group while the outer task group was in the process of shutting down.)
Overall, this follows from this behaviour of asyncio tasks:
t=asyncio.create_task(my_fn())t.cancel()
This will not runmy_fn()
at all, not even to the firstawait
within it. This is despite the fact that thedocs forasyncio.Task.cancel()
say:
This arranges for aCancelledError exception to be thrown into the wrapped coroutine on the next cycle of the event loop. The coroutine then has a chance to clean up or even deny the request ...
I looked over old issues to see if this had been reported and found the#98275 which suggested changing the docs to warn about this, but it has since been closed.
Personally, I would say that the behaviour ofcreate_task
andTask.cancel()
are incorrect, but looking back at that discussion I can see that this is a matter of opinion. However, I think the task group behaviour really does need to be fixed. It's hard to see how this could be reliably done with the current task behaviour, so I think that gives some weight that it really is the undelying issue.
Perhaps, as a compromise, there could be a new parameterasyncio.create_task(..., run_to_first_await=False)
, which can be set toTrue
byTaskGroup
and other users wanting robust cancellation detection?
CPython versions tested on:
3.12
Operating systems tested on:
Windows
Metadata
Metadata
Assignees
Projects
Status