-
-
Notifications
You must be signed in to change notification settings - Fork 32k
asyncio.timeout(0) and asyncio.timeout(-1) cancellation is delayed #95051
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
This also makes refactoring |
Sorry, deleted my last comment since it was wrong. I'm going to try to dive into this a little to get some understanding. |
This is a race condition. Two things need to happen on the same event loop iteration, but it turns out A happens before B, and not B before A. I don't think asyncio has a spec anywhere saying this is wrong, so a legitimate response might be to just live with it. Maybe it's confusing to users though so we want to do something about it. The event loop maintains two callback lists:
When processing a loop step, the event loop looks at
So this explains the issue:
A couple of proposals how to handle this: A. do nothing, leave it up to the event loop how to resolve cases like this My personal preference might be D, and then A. @gvanrossum I know you've been in this space recently, would appreciate your feedback. |
This is the uvloop behavior |
I think it's probably too late in the 3.11 release cycle to touch something this low level in the event loop. I think tweaking |
Tin, thanks for the deep dive! I have to agree with Thomas that we don't want to touch the event loop or call_at. But since timeout is new, I'd be okay if we tweaked that (i.e., B). If either of you want to produce a PR that includes a unit test demonstrating the need for a fix, I'd approve it and it might not be hard to convince Pablo to allow it in 3.11, but I think A is also acceptable for 3.11 (race conditions gonna race). For 3.12 we might be able to do C or D. |
I'm not an asyncio expert, but I don't think we should go with D. Prepending may cause starvation in certain scenarios (the callbacks at the back of the deque may never get to run). |
So out of curiosity I checked out what UVLoop does, since in my experience anyone who's serious about asyncio runs it anyway. They prepend the scheduled callbacks (well, lib uv does under the hood), so they do D. @graingert 's example works expectedly there. This increases my confidence in that approach, since I'm already using it in production ;) @Fidget-Spinner Not sure how callbacks at the end of the deque not running is any worse than callbacks in the heapq. |
…have already expired are deliverered promptly (#95109) Co-authored-by: Kumar Aditya <59607654+kumaraditya303@users.noreply.github.com>
… that have already expired are deliverered promptly (pythonGH-95109) Co-authored-by: Kumar Aditya <59607654+kumaraditya303@users.noreply.github.com> (cherry picked from commit 0c6f898) Co-authored-by: Thomas Grainger <tagrain@gmail.com>
So I did some more digging, especially through libuv (the library that powers NodeJS and uvloop). As far as I can see, libuv does what I'm proposing. On each iteration of the loop, expired timers (= callbacks with a deadline that is before now) get run first, and then the |
Fixed by #95109 |
Bug report
I expect this to raise a TimeoutError:
Instead I have to add an extra
asyncio.sleep(0)
to get a TimeoutErrorYour environment
The text was updated successfully, but these errors were encountered: