-
-
Notifications
You must be signed in to change notification settings - Fork 335
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
Maybe move_on_after(0)/fail_after(0) should immediately cancel their scope #320
Comments
If If we want to support a slop factor too, like
|
I suspect we'll end up promoting I think it is definitely not proven yet that |
Fixes python-trio#320. Now that python-trio#901 is implemented, the concerns discussed in python-trio#835 don't apply.
It's tempting to think that you can fake a "check once, but don't block" semantics by setting up a 0-timeout cancel scope:
This is not how Trio's cancellation semantics are supposed to work – there's no guarantee that
accept
won't raiseCancelled
before it even attempts the operation.@sorcio points out in chat that this actually works surprisingly well though. In fact, it probably succeeds deterministically right now (!), because
fail_after(0)
schedules its cancel scope to be cancelled "immediately", but since it uses a timeout to do this it doesn't actually happen until Trio processes timeouts, which it doesn't do until after this task yields. And if you look attrio._socket._SocketType.accept
... it doesn't yield until after trying a non-blockingaccept
. So right now things work by accident.This is dangerous, because this is a tempting thing to try, and the sort of thing where people might end up depending on it because it seems to work. I've been tempted and stopped b/c I thought it through and realized it wouldn't work – but we can't expect everyone to have my level of familiarity with the subtleties of Trio's semantics :-) – and @sorcio's tried it and found it worked...
So, if we want to keep the current semantics, maybe we should special-case
move_on_after(0)
andfail_after(0)
to immediately cancel the scope they create, instead of going through the normal timeout machinery. Of course it would still be possible to get a similar effect withmove_on_after(1e-12)
, but then at least it's obvious that you're doing something potentially race-y.Alternatively, could we change our semantics to make this work? The obvious problem would be IOCP on Windows. If you use IOCP to implement
accept
, then the underlying operation is actually "hey Windows, please start anaccept
running in the background", and then blocking and going through the event loop to check if it's done. In practice, if anaccept
is ready immediately, then it'll probably complete either synchronously (IOCP operations are allowed to do that) or at least before we notice the timeout and callCancelIoEx
. But it makes me nervous? I dunno, maybe this would actually be fine.The bigger problem is that the semantics of "don't deliver cancellations until a task actually blocks rather than just checkpoints" are fundamentally problematic: if you have some heavy computational work to do in the background, then the standard way is to go ahead and do it and just make sure to checkpoint frequently so other tasks can run. If we start delaying cancellations in general, then it becomes impossible to cancel a task like this.
So if we were going to do this, it would have to a specific thing the user had to request, I think – and then it would be the user's job to make sure that you only use it for operations that either succeed or block quickly, like
accept
.with trio.move_on_when_blocks(): ...
. That's... not totally implausible. Certainly it'd be possible to implement, so long as we wrote down a clear definition of the difference between a checkpoint and blocking – which I'm a bit reluctant to do, because adding more distinctions adds complexity. Though we've already opened that door somewhat withtrio.testing.wait_all_tasks_blocked
… but that's intrio.testing
plus it lets you specify a slop factor exactly to handle things like IOCPaccept
needing a few microseconds to be processed.Probably the immediate thing to do is to add the special-case to make zero timeouts actually instantaneous, to keep our options open, and then continue to think about this.
Cross-ref: #242; in some sense this and that are alternatives, since they both potentially provide more general ways to handle
XX_nowait
.The text was updated successfully, but these errors were encountered: