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:
fetch
yields a Future
.Future
is added to the IOLoop
via http://www.tornadoweb.org/en/stable/ioloop.html#tornado.ioloop.IOLoop.add_futureFuture
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?
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.