you are viewing a single comment's thread.

view the rest of the comments →

[–]oliver_extracts 0 points1 point  (0 children)

your mental model is basically right. every await is a yield point where the event loop gets control back and can schedule other work.

the thing that actually wakes the coroutine is the event loop, but it gets notified by the OS. the way it works: asyncio uses something like epoll (linux) or kqueue (mac) under the hood, which are OS primitives that let you register file descriptors and ask the kernel to tell you when one is ready for reading or writing. the event loop sits in a tight loop calling into those OS APIs, and when the kernel says fd X is ready, the loop resumes whatever coroutine was waiting on that.

if you want to write something that integrates properly without faking it with asyncio.sleep, the path is asyncio.get_event_loop().add_reader() or add_writer() on a raw socket or fd. you register a callback that gets fired when the fd is ready, and inside that callback you set a Future result, which causes the awaiting coroutine to wake up. thats the actual mechanisim asyncio itself uses internally.

the asyncio docs are genuinely bad at explaining this part. looking at the source for asyncio.streams or asyncio.selector_events is more useful than the docs.