Tokio doesn't use epoll/kqueue/select for async io?

I always thought tokio, for rust, is implemented with OS native poll fd for async io:

  • epoll for linux
  • kqueue for mac
  • legacy select for windows, or iocp
  • legacy select/poll for other unix

Because in c/c++ this is very basic for a multi-threaded, non-blocking framework, such as libevent, libuv, asio, etc.

Until I read this part: tokio::fs - Rust

"Be aware that most operating systems do not provide asynchronous file system APIs. Because of that, Tokio will use ordinary blocking file operations behind the scenes. This is done using the spawn_blocking threadpool to run them in the background."

So file io is actually blocking io + thread pool? Did I mis-understood something?

Not all IO is the same. Different kinds of FDs/handles have different amounts of support for non-blocking access. File systems are generally the least compatible; while some platforms do have some amount of async file read/write support, it hasn’t been available and broad enough for Tokio to already be supporting it.

However, looking at Tokio's issue tracker, it seems there is recent progress on async file IO via io_uring on Linux:

Even then, there are many more filesystem operations (e.g. deleting a file) which are not expressed in terms of reading/writing bytes and must each have non-blocking support implemented in the OS and async support implemented in Tokio, before Tokio could remove that disclaimer entirely from their documentation.

1 Like

Thank you for explain.

I just found this: Tokio interaction with Epoll object - #2 by bjorn3

It seems for tcp network Tokio "is" using epoll as a non-blocking io backend.


Update: But why tokio only support non-blocking IO for tcp/network fd? Why not also support them with normal file fd? At least in linux, tcp and normal file fd can be handled in the same way.

It's a bit surprising to see that you mention asio without mentioning that it could only do asynchronous operations on Windows and, sometimes, on Linux (on many Android versions io_uring is not supported, e.g). It's written very explicitly in the documentation!

Besides the fact that C++ libraries normally don't support async io on files while Rust does the best it could do? No.

I could use wrong words or concepts.


By "async file io", I think I mean:

  1. Register a epoll/poll/kqueue fd for all other file, tcp fd handled by tokio.
  2. Set "non-blocking" flags for a file fd when open or create it.
  3. Register the file fd into the epoll/poll/kqueue with a callback (in c-style) function, wrap it into the "Future::poll" in rust
  4. Schedule the file IO with epoll/kqueue event, thus we can get a non-blocking, single-thread IO on multiple files and tcp streams.

But I realized that, maybe user should register the epoll/kqueue by themself, and do all the above work manually. Tokio's default implementation is using thread pool.

It uses something that works.

Not sure what do you want to achieve here. Attempt to pass regular file in epoll simply returns EPERM

There are exist entirely different mechanism that allows one to use async I/O on regular files, but not all common versions of Linux support it.

1 Like

The documentation for poll says:

Being "ready" means that the requested operation will not block;
thus, poll()ing regular files, block devices, and other files with
no reasonable polling semantic always returns instantly as ready
to read and write.

And epoll doesn't support regular files at all:

EPERM The target file fd does not support epoll. This error can
occur if fd refers to, for example, a regular file or a
directory.


And regular files support random access (pread), which is conceptually incompatible with the idea of readiness at the file descriptor level.

1 Like

Tokio does not use regular files with epoll because it doesn't work. According to epoll, files are always "ready" for reading or writing, even if such operations actually block. Other than io_uring, there is simply no such thing as "asynchronous file IO", and so we do not support things that do not exist.

5 Likes

Actually you can usually register a normal file with epoll. It just doesn't behave in a useful way.

Isn't the AIO interface async IO? (With the same kind of buffer lifetime challenges as other completion based IO)

So when does epoll_ctl return EPERM, if it doesn't do so for all regular files?

Open your own link. Read this:

Does it answer your question?

3 Likes

No clue.

thank you for this.

seems I mis-understood it for quite a long time, several years I think .........

thank you!

Thank you!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.