You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
We have a situation where the cancellation of a cancel scope can't be delivered to a task, because it's not yet started. So, anyio will retry the delivering of the cancellation to this task using call_soon. (I'm using the asyncio backend.)
This situation causes a race condition where the task is started anyway, even able to create a new task group, spawn sub tasks in there, have these subtasks raise an exception and only after that realizing it was actually cancelled raising an ExceptionGroup of both a CancelledError and our own exception.
I'd like to be able to check whether cancellation is scheduled for our current task, and if so, don't proceed doing other stuff, but wait for the cancellation to take place.
Apparently, adding an await sleep(0) isn't sufficient as a check point to allow the cancellation to take place. The cancellation seems to be scheduled only when doing await sleep(0.1) or something like that, but a random sleep value doesn't feel right. As a workaround, I found that the following seems to work:
However, I'd like to know whether doing something like this makes sense, or whether there's a bug somewhere? I'd expect for instance that entering a task group (create_task_group().__aenter__()) would be considered a checkpoint where cancellation can take place, and where we could do exactly the above code.
I've been looking to create a small reproducer script, but so far failed to reproduce the more complex scenario we currently have.
The text was updated successfully, but these errors were encountered:
Actually, the checkpoint_if_cancelled function does more or less what I want, but the sleep(0) in there is not sufficient, because it could be that the cancellation is only scheduled, so asyncio will not yet throw a CancelledError in there.
What I have now as a workaround that works both on master and on 3.6.2 is this:
asyncdefwait_for_cancellation_if_cancelled() ->None:
""" If cancellation is scheduled for the current task, wait until the cancellation takes place and propagate the `CancelledError` in that case. This is needed in some situation to avoid doing stuff if we know that we're going to be cancelled. When a cancel scope (or task group) gets cancelled, the cancellation will be delivered to all containing tasks. If these tasks are not yet started, the cancellation will be scheduled using `call_soon`. This means that at some point, a task can be started, knowing that it will be cancelled *very soon*, possibly at the first `await` checkpoint, if it takes long enough for the `call_soon` callbacks to be processed. In this case, using `await sleep(0)` is typically not long enough for all `call_soon` callbacks to run. """try:
# Anyio 4.0 (master branch.)fromanyio.lowlevelimportget_async_backendexceptImportError:
# Anyio 3.6.2 (implementation only works for asyncio).fromanyio._backends._asyncioimport_task_states, current_taskdefis_cancelled() ->bool:
# Partly copied from `checkpoint_if_cancelled` in Anyio's asyncio# backend.task=current_task()
iftaskisNone:
returnFalsetry:
cancel_scope=_task_states[task].cancel_scopeexceptKeyError:
returnFalsewhilecancel_scope:
ifcancel_scope.cancel_called:
returnTrueelifcancel_scope.shield:
breakelse:
cancel_scope=cancel_scope._parent_scopereturnFalsecancelled=is_cancelled()
else:
cancelled=get_async_backend().current_effective_deadline() <0ifcancelled:
# Will soon raise `CancelledError`.awaitanyio.sleep_forever()
I notice that this fix here: https://github.com/agronholm/anyio/pull/485/files is not yet part of the latest release on PyPI, possibly because it's for Anyio 4.0? Because of that, in the above snippet I can't use current_effective_deadline on 3.6.2.
We have a situation where the cancellation of a cancel scope can't be delivered to a task, because it's not yet started. So, anyio will retry the delivering of the cancellation to this task using
call_soon
. (I'm using the asyncio backend.)This situation causes a race condition where the task is started anyway, even able to create a new task group, spawn sub tasks in there, have these subtasks raise an exception and only after that realizing it was actually cancelled raising an
ExceptionGroup
of both aCancelledError
and our own exception.I'd like to be able to check whether cancellation is scheduled for our current task, and if so, don't proceed doing other stuff, but wait for the cancellation to take place.
Apparently, adding an
await sleep(0)
isn't sufficient as a check point to allow the cancellation to take place. The cancellation seems to be scheduled only when doingawait sleep(0.1)
or something like that, but a random sleep value doesn't feel right. As a workaround, I found that the following seems to work:However, I'd like to know whether doing something like this makes sense, or whether there's a bug somewhere? I'd expect for instance that entering a task group (
create_task_group().__aenter__()
) would be considered a checkpoint where cancellation can take place, and where we could do exactly the above code.I've been looking to create a small reproducer script, but so far failed to reproduce the more complex scenario we currently have.
The text was updated successfully, but these errors were encountered: