poll() is the executor’s API to the future - it’s how it’s able to determine the status of the future (done, not done, errored). Asynchronous work is typically done, well, asynchronously .
Future represents some work executing on a threadpool, for example, then that work can be progressing concurrently.
Future only tells the executor whether it’s done or not (via
poll()), and it needs to have a way to determine that by setting up some communication with the threadpool.
Future is waiting on bytes to be available for reading on the socket, then that
Future will have registered an event notification to be delivered by whatever OS kernel API when there’s some data to be read. That work is also happening concurrently, and not even in userland in this case.
So it’s better to think of
poll() as just the API an executor has to ask the
Future for its status.
I think I sort of answered this above as well. It’s not that the
Future's work makes progress per say, but rather it’s how the executor can drive the future to completion by asking it for its status, and if it’s not ready, keeping it stored somewhere until a task notification has been received, and the future is re-polled for its status. The most interesting case is when a future returns
Ok(Async::NotReady) - to avoid busy polling it, as in the dummy executor I showed earlier, there’s machinery for the future to return NotReady but to also register a notification such that when the asynchronous operation makes progress, the future is re-polled.
For a deep dive on how tokio interoperates with futures, take a look at https://cafbit.com/post/tokio_internals/. It should make the connection between a future, a task (i.e. notification mechanism), and an executor (tokio event loop in this case) a bit more concrete.