Paul
Paul

Reputation: 6737

python 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?

Upvotes: 4

Views: 261

Answers (1)

Andrew Svetlov
Andrew Svetlov

Reputation: 17376

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 the 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.

Upvotes: 4

Related Questions