Search code examples
rustasync-awaitnonblocking

Why do I need async / await if I already have non-blocking IO like epoll / kqueue / IOCP?


I have experience with the non-blocking IO library epoll and its high level abstraction libuv, so when I started learning Rust, I looked for Rust equivalents and found Mio. Mio works the way I understand about non-blocking IO and is familiar to me. I can easily use this kind of API to build high performance server applications using several threads or even a single thread.

Rust 1.39 brought the async / await syntax. I have read some articles and documentation on it and it feels like coroutines. I understand that .await can be used to yield CPU to do other things in the same thread, but there is no easy way to dynamically schedule code to run in that thread, it doesn't work in an evented way like epoll, with which I can register events in advance and get notified when those events happen.

The example for async / await of opening and reading files doesn't make sense to me, because in that example after .await I have nothing to do but wait for the content to be available, so I am still blocking and not async at all.

If I use non-blocking IO like Mio or unsafe epoll, do I still need async / await? If not, in what cases should I use async / await?


Solution

  • If I use non-blocking IO like Mio or unsafe epoll, do I still need async / await?

    No, you don't need async / await: you have code that does what you want, you are happy with it, and it doesn't use async / await. There's no apparent reason for you to change your code. You state this in multiple ways:

    Mio works the way I understand

    [Mio] is familiar to me.

    I can easily use this kind of API

    The same style of question could be asked about functions. Functions aren't strictly needed because we could copy-paste code everywhere it's used. The code does what someone wants, someone is happy with it, and it doesn't make use of functions. There's no apparent reason for this developer to change their code.


    Yes — in general, most Rust developers will use async / await when they are trying to write asynchronous code (and maybe even when they shouldn't). These keywords are mostly syntax sugar for creating a future. Futures have existed in the Rust ecosystem for a long time before the special syntax was introduced, so even the syntax isn't required. However, it does make creating futures simpler.

    Futures are an abstraction of asynchronous work that are ultimately driven by an executor. One of the things that it's possible to put underneath the abstraction is an event-based IO library. The library notifies the executor that something has changed with one of the IO handles the library manages, which causes the executor to resume the future, which may realize that it's complete.

    Mio is used by some of the most common futures-related crates like tokio or async-std. The Mio documentation even suggests using it with futures:

    This is a low level library, if you are looking for something easier to get started with, see Tokio.

    See also: