Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tokio::io::bsd::Aio can cause multithreaded runtime to busy-loop #4728

Open
asomers opened this issue May 30, 2022 · 2 comments
Open

tokio::io::bsd::Aio can cause multithreaded runtime to busy-loop #4728

asomers opened this issue May 30, 2022 · 2 comments
Labels
A-tokio Area: The main tokio crate C-bug Category: This is a bug. M-io Module: tokio/io

Comments

@asomers
Copy link
Contributor

asomers commented May 30, 2022

Version
Tokio git master at f6c0405

Platform
FreeBSD 14.0-CURRENT amd64

Description

Background
The funny thing about AIO is that you don't register it with kevent. Instead, you register it by setting a certain field within the struct aiocb that you submit with a syscall like aio_write. When it's ready, kevent will return an event with EVFILT_AIO set. The user must then call aio_return to free the kernel resource, or else kevent will keep reporting that event as ready. As written now, the aio_return call happens outside of Tokio, in the tokio-file crate, in a custom Future's poll method.

Problem
With the multi-threaded runtime, it's possible that the future's task is on one thread, but the kqueue it's registered to is being polled by a different thread. When the kernel notifies the polling thread, that thread will signal (how I'm not sure) the thread with the future. Then it will go back to sleep waiting for more events. But if the thread with the future doesn't poll it quickly, then polling thread will immediately get woken again. This can lead to many more kevent calls than should be necessary. In my application, I see up to 200x more kevent calls than aio_return calls.

Possible Solutions

  • Move the struct aiocb into the IO Driver, and arrange for the IO driver to promptly call aio_return upon notification, storing the result.
  • Use EV_ONESHOT to make the aio operation edge-triggered. I'm not sure if this works with POSIX AIO.

Example
This test case will obviously never finish. Ideally it would simply hang. But because nothing ever calls aio_return, the IO driver will busy loop around kevent, and it will spin the cpu.

    #[tokio::test(flavor = "current_thread")]
    async fn busy() {
        use futures::FutureExt;

        let f = tempfile().unwrap();
        let fd = f.as_raw_fd();
        let aiocb = AioCb::from_fd(fd, 0);
        let source = WrappedAioCb(aiocb);
        let mut poll_aio = Aio::new_for_aio(source).unwrap();
        (*poll_aio).0.fsync(AioFsyncMode::O_SYNC).unwrap();
        let some_aio_future = FsyncFut(poll_aio);
        tokio::pin!(some_aio_future);
        
        // poll it once
        assert!(some_aio_future.as_mut().now_or_never().is_none());
        
        std::future::pending::<()>().await;
    }
@asomers asomers added A-tokio Area: The main tokio crate C-bug Category: This is a bug. labels May 30, 2022
@Darksonn Darksonn added the M-io Module: tokio/io label May 30, 2022
@asomers
Copy link
Contributor Author

asomers commented Jun 1, 2022

I've confirmed that using EV_ONESHOT fixes the problem. That means that the major fix will happen in the tokio-file crate, but Tokio will still need a doc and tests fix. The hard part is that this involves C unions, and backwards-incompatible changes to libc. Sigh.

@Darksonn
Copy link
Contributor

Darksonn commented Jun 1, 2022

I guess documenting that you must use EV_ONESHOT is a possible solution.

asomers added a commit to asomers/nix that referenced this issue Jun 4, 2022
asomers added a commit to asomers/nix that referenced this issue Jun 4, 2022
Also, disallow using SigevNotify::SigevThreadId on musl.  I don't think
it ever worked.

Depends on rust-lang/libc#2813
Blocks tokio-rs/tokio#4728
asomers added a commit to asomers/nix that referenced this issue Jun 4, 2022
Also, disallow using SigevNotify::SigevThreadId on musl.  I don't think
it ever worked.

Depends on rust-lang/libc#2813
Blocks tokio-rs/tokio#4728
asomers added a commit to asomers/nix that referenced this issue Aug 11, 2023
Also, disallow using SigevNotify::SigevThreadId on musl.  I don't think
it ever worked.

Depends on rust-lang/libc#2813
Blocks tokio-rs/tokio#4728
asomers added a commit to asomers/nix that referenced this issue Aug 11, 2023
Also, disallow using SigevNotify::SigevThreadId on musl.  I don't think
it ever worked.

Depends on rust-lang/libc#2813
Blocks tokio-rs/tokio#4728
asomers added a commit to asomers/nix that referenced this issue Aug 12, 2023
Also, disallow using SigevNotify::SigevThreadId on musl.  I don't think
it ever worked.

Depends on rust-lang/libc#2813
Blocks tokio-rs/tokio#4728
asomers added a commit to asomers/nix that referenced this issue Aug 20, 2023
Also, disallow using SigevNotify::SigevThreadId on musl.  I don't think
it ever worked.

Depends on rust-lang/libc#2813
Blocks tokio-rs/tokio#4728
asomers added a commit to asomers/nix that referenced this issue Aug 20, 2023
Also, disallow using SigevNotify::SigevThreadId on musl.  I don't think
it ever worked.

Depends on rust-lang/libc#2813
Blocks tokio-rs/tokio#4728
asomers added a commit to asomers/nix that referenced this issue Aug 26, 2023
Also, disallow using SigevNotify::SigevThreadId on musl.  I don't think
it ever worked.

Depends on rust-lang/libc#2813
Blocks tokio-rs/tokio#4728
github-merge-queue bot pushed a commit to nix-rust/nix that referenced this issue Aug 27, 2023
* Allow setting kevent_flags on struct sigevent

Also, disallow using SigevNotify::SigevThreadId on musl.  I don't think
it ever worked.

Depends on rust-lang/libc#2813
Blocks tokio-rs/tokio#4728

* Inline libc::sigevent

Because the PR to libc is stalled for over one year, with no sign of
progress.
asomers added a commit to asomers/tokio-file that referenced this issue Aug 29, 2023
So tokio-file can be used by a multithreaded tokio runtime.

See also tokio-rs/tokio#4728
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-tokio Area: The main tokio crate C-bug Category: This is a bug. M-io Module: tokio/io
Projects
None yet
Development

No branches or pull requests

2 participants