Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Strictly speaking, it's not tied to any particular method. It depends on your executor. That said, the most popular executor does use epoll/kqueue/iocp. (tokio)


Doesn't the executor need to be aware of all the different mechanisms that can be used to poll, and so there's an implicit coupling between the async function implementation and the executor?

For example, socket.read() might return a future that represents a read on a file descriptor. I don't know the internals of Rust's async support at all, but presumably the future is queued up and exposes some kind of trait that Tokio et al can recognize as being an FD so it can be polled on using the best API such as epoll_wait() or whatever.

But let's say there's some kernel or hardware API or something that has a wait API that isn't based on file descriptors, and I implement my own async function get_next_event() that uses this API. Do I need to extend Tokio, or the Rust async runtime API, to make it understand how to integrate this into its queues? In a non-FD case, wouldn't it have to spawn a parallel thread to handle waiting for that one future, since it can't be included in epoll_wait()?


I slightly mis-spoke in a sense, yeah. This stuff has changed a bunch over the last few years :)

So, futures have basically two bits of their API: the first is that they're inert until the poll is called. The second is that they need to register a "waker" with the executor before they return pending. So it's not so much that the executor needs to know details about how to do the polling; but the person implementing socket.read() needs to implement the future correctly. It would construct the waker to do the right thing with epoll. Tokio started before this style of API existed, and so bundles a few concepts in the current stack (though honestly, an integrated solution is nicer in some ways, so I don't think it's a bad thing, just that it makes it slightly easier to conflate the pieces since they're all provided by the same package.)

Async/await, strictly speaking, is 100% agnostic of all of this, because it just produces stuff with the Futures interface; these bits are inside the implementation of leaf futures. And executors don't need to know these details, they just need to call poll at the right time, and in accordance with their wakers.

I can't wait until the async book is done, it's really hard remembering which bits worked which way at which time, to be honest.


> And executors don't need to know these details, they just need to call poll at the right time, and in accordance with their wakers.

Is this true? Essentially this is claiming that an executor does not need to use mio (epoll/kqueue/...) to be able to execute futures that do async network i/o.

So who uses mio? Would each type implementing the Future trait use mio internally as a private detail? That is, using two such future types, would they maintain multiple independent kqueues and the executor isn't able to put them both in one?


mio is useful when you’re writing an application that runs on windows/Linux/Mac. But you can use futures on any platform, including embedded ones. Those would be coded against whatever API the system offers. There’s an embedded executor, for example.

Tokio uses mio to implement its futures that do async IO, so if you use Tokio, you use mio. You don’t have to use Tokio, though it is the most popular and most battle tested.

Many futures don’t do IO directly; for example, all of the combinator futures. Libraries can be written to be agnostic to the underlying IO, only using the AsyncRead/AsyncWrite traits, for example.


The TL;DR: is that, while the `std::future::Future` trait is generic, the actual type that implements this trait is often tied to a particular executor.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: