Search code examples
asynchronoustornadopython-asyncioconcurrent.futuresioloop

How do asynchronous tasks say they're "done" if there's one thread?


I've written asynchronous programs using Tornado and asyncio, however I've realized I don't understand how asynchronous tasks say they're done.

For example, let's take a look at the asynchronous fetch in http://www.tornadoweb.org/en/stable/guide/async.html#examples.

My understanding thus far is:

  1. The handler is suspended when fetch yields a Future.
  2. The Future is added to the IOLoop via http://www.tornadoweb.org/en/stable/ioloop.html#tornado.ioloop.IOLoop.add_future
  3. The Future finishes, and the IOLoop schedules the coroutine to be reanimated so it can complete.

What I don't understand is how the Future in step 3 "finishes" and invokes its done callback. I thought there was only one thread, so how would the Future "work in the background" and get control so it could invoke the callback?


Solution

  • The IOLoop opens a socket to the remote server you're fetching from, and adds that socket to a list of file descriptors on which it's waiting for IO using epoll or a similar system call.

    Whenever the loop is not executing your code -- for example, when your handler is paused by yield, the loop is waiting for IO, here:

    https://github.com/tornadoweb/tornado/blob/master/tornado/ioloop.py#L862

    When it receives an IO event -- for example, when the remote server sends some response bytes -- Tornado finds the callback that was waiting for that event and executes it.

    For an example implementation of an event loop, see A Web Crawler With asyncio Coroutines.