-
Notifications
You must be signed in to change notification settings - Fork 149
On asyncio, Event.set()
sometimes fails to notify all waiting tasks
#536
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
I'm really confused now. If a task is cancelled while blocked on |
My rationale for why this is a problem: Assume that the event is associated with reliably transferring some data from task A to task B. For example, a rendezvous object. A sees that somebody (B) is waiting, thus it stores the data, sets the event, and continues on its merry way. B's If that doesn't happen when B gets cancelled, suddenly nobody is responsible for the data in question. You can't fix this by teaching the While it's certainly possible to add higher-level workarounds like an atomic and non-waiting "get the data out of this object if there happens to be any in there" method, that doesn't match every other |
Don't you think those guarantees go right out the window when tasks start getting cancelled (rather than the memory object streams being closed)? And OP's argument was that, if a task was cancelled after being scheduled, AnyIO should somehow override asyncio's normal cancellation mechanism to allow the task to continue instead of raising a cancellation error like it normally would? |
Well they don't go out the window when you talk to network connections, so why should they do so when using a memory stream? (Besides, well that's just the first example I could think of, I'm sure there are others where this may be an issue.) If fixing / working around this by way of the AnyIO wrapper is too difficult / not possible / degrades performance too much / too much work for too little gain / …, given asyncio's current cancellation semantics, the alternate solution would be to (a) document that yes this is a problem with no generic workaround by (mis)design, use Trio if you need a way to get guaranteed behavior, and (b) teach asyncio sane cancellation semantics so that this gets fixed long term. |
If you're reading from a socket in a task, and the task is scheduled to be resumed and then gets cancelled, another task could take over and start receiving data from it without anything being lost. As for memory streams, we already handle this problem (here) by ignoring the first cancellation so as not to lose the data. |
Event.wait()
sometimes fails to notify all waiting tasksEvent.set()
sometimes fails to notify all waiting tasks
oops, sorry—that's just a typo in the subject line. fixed now.
sort of—those lines handle half of the problem for memory object streams. as i mentioned briefly in the top post, memory object streams still exhibit the other half of the bug. in more detail: consider a memory object stream pair with
what i mean to point out is that the current code that handles the special case of (1.) is the symptom of a lower-level problem. one should not have to patch with case (2.) in particular, the problem is within AnyIO so one can't apply a band-aid patch to it without forcing users to install a patched version of the library. it may be clearer if you have time to peek at #537. in that proposed changeset, i essentially remove the asyncio-only band-aid from |
hi!
if:
Event.wait()
Event.set()
is calledwait()
's cancel scope is cancelled before the event loop schedules Athen, on the asyncio backend only, task A's
wait()
raises cancelled instead of getting notified of the event.in contrast, on the trio backend (and on pure trio),
Event.set()
still notifies all waiters (including task A) in this situation. trio does this by implementingEvent.wait
viawait_task_rescheduled
.here is a small reproducer:
test_event_wait_before_set_before_cancel
in 65d74e4.assert wait_woke
fails on asyncio only.this problem is also the direct cause of another bug:
MemoryObjectSendStream.send(item)
can raise cancelled afteritem
has been delivered to a receiver!1 thisMemoryObjectSendStream.send
issue is just thesend_event.wait()
case to #146'sreceive_event.wait()
case, i believe—the underlying issue for both seems to be thisEvent.wait()
problem2.a couple relevant comments from njsmith: #146 (comment), #147 (comment).
Footnotes
raising cancelled after a receiver gets the item is a violation of trio's cancellation semantics for API in the
trio
namespace (https://trio.readthedocs.io/en/stable/reference-core.html#cancellation-and-primitive-operations). sinceanyio
is documented as followingtrio
's cancellation model, i would guess thatanyio
is also intended to adhere to this (but in theanyio
namespace)? if so, i think it would be good to document this at https://anyio.readthedocs.io/en/stable/cancellation.html and to document the couple rare exceptions to this rule (e.g. https://trio.readthedocs.io/en/stable/reference-io.html#trio.abc.SendStream.send_all) too. ↩nit: Object stream randomly drops items #146 actually had two underlying issues: the problem that
MemoryObjectReceiveStream.receive()
didn'tcheckpoint_if_cancelled()
before tryingreceive_nowait()
(on all backends), as well as thisEvent.wait()
issue (on asyncio only). ↩The text was updated successfully, but these errors were encountered: