expLog

The Asyncio Scheduler

If you read the PEP 3156 closely, you'll read a brief description of the task scheduler introduced with it (emphasis mine).

The proposal includes a pluggable event loop (…) a higher-level scheduler based on yield from (PEP 380). (…) The scheduler is not pluggable; pluggability occurs at the event loop level, and the standard scheduler implementation should work with any conforming event loop implementation. (In fact this is an important litmus test for conforming implementations.) (…) For interoperability between code written using coroutines and other async frameworks, the scheduler defines a Task class that behaves like a Future. A framework that interoperates at the event loop level can wait for a Future to complete by adding a callback to the Future. Likewise, the scheduler offers an operation to suspend a coroutine until a callback is called. (…) The scheduler has no public interface. You interact with it by using yield from future and yield from task. In fact, *there is no single object representing the scheduler – its behavior is implemented by the Task and Future classes* using only the public interface of the event loop, so it will work with third-party event loop implementations, too.

The lack of details around the behavior of the scheduler as well as the unexpected behavior described in Latency in Asynchronous Python made me fairly curious about the internals of the scheduler: leading to this post.

The heart of the scheduler revolves around Tasks: every time a Task is created,

The next interesting part is how it decides if a coroutine is finished, should yield to allow something else to run, or is simply returning a value that can return immediately.

I have to admit to being very confused the first time I traced a call with an async generator that yielded values and finding out that the outer coroutine never yielded. The magic is in asyncio.tasks.Task.__step, which checks the return values from calling coroutine.send(None) (called result):

misc links: https://docs.python.org/3/howto/clinic.html

view source