Paul Paul - 4 days ago 6
Python Question

pyton 3.5 asyncio how interpreter handles 'suspended' corutines

I'm new to asyncio and trying to understand how it actually works.

Lets say we have two corutines and one of them looks like this:

async def f():
await sleep(10)
print('something')


Instead of
sleep()
could be any IO operation, obviously.
If I understand this code right, we starting to execute
sleep(10)
and switching context to the some other couroutine (if it exists).

But how does interpreter 'counts' that 10 seconds if this coroutine has been suspended? Or how does interpreter handle some IO response, if it happened when coroutine is suspended?

Answer

Internally asyncio.sleep() returns a Future object. The future's value will be set after timeout expiring.

Every coroutine is executed by asyncio.Task. The future is bubbled up to task runner (Task._step() actually). The runner adds a callback to bubbled future for waking up itself when future will be done.

sleep() implementation is trivial:

@coroutine
def sleep(delay, result=None, *, loop=None):
    """Coroutine that completes after a given time (in seconds)."""
    if delay == 0:
        yield
        return result

    if loop is None:
        loop = events.get_event_loop()
    future = loop.create_future()
    h = future._loop.call_later(delay,
                                futures._set_result_unless_cancelled,
                                future, result)
    try:
        return (yield from future)
    finally:
        h.cancel()

Task runner is much more complex beast but it's source code is still readable: https://github.com/python/asyncio/blob/master/asyncio/tasks.py#L223-L300

Every blocking IO returns a future too (maybe from very deep internal call). When IO waiting is done the given value (or exception) is assigned to the future, task runner is woken up and suspended coroutine is resumed.

Comments