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

Support waiting on POLLPRI events on Linux #4885

Closed
ColonelThirtyTwo opened this issue Aug 5, 2022 · 5 comments · Fixed by #5566
Closed

Support waiting on POLLPRI events on Linux #4885

ColonelThirtyTwo opened this issue Aug 5, 2022 · 5 comments · Fixed by #5566
Labels
A-tokio Area: The main tokio crate C-feature-request Category: A feature request. M-io Module: tokio/io

Comments

@ColonelThirtyTwo
Copy link

ColonelThirtyTwo commented Aug 5, 2022

Is your feature request related to a problem? Please describe.
/proc/self/mounts raises a priority event when there is a change in mount points (something is mounted or unmounted). There are probably other similar system-related files that do this as well. There appears to be no way of waiting for these in Tokio.

Describe the solution you'd like
Add a Priority variant to tokio::io::Interest and implement support for it in tokio::io::unix::AsyncFd.

Describe alternatives you've considered
Polling for changes manually. Writing my own select/poll loop in a blocking thread.

Additional context
None

@ColonelThirtyTwo ColonelThirtyTwo added A-tokio Area: The main tokio crate C-feature-request Category: A feature request. labels Aug 5, 2022
@Noah-Kennedy
Copy link
Contributor

I think it may be worthwhile for us to think about support for other event types in epoll and kqueue in general actually.

@Noah-Kennedy
Copy link
Contributor

@ColonelThirtyTwo A workaround for this is that an epoll fd is itself pollable, so you can create an epoll for watching event types we do not support yet, and put it in an AsyncFd to get notified when it has events ready to be read.

@ColonelThirtyTwo
Copy link
Author

Apparently mio does listen for priority events if you use the readable interest, though you'll get a single read ready event at the beginning of watching.

@folkertdev
Copy link
Contributor

as a datapoint, this is what we do in ntpd-rs:

We have an existing udp socket. Based on that socket we create an additional "exceptional condition" socket that becomes readable when there is something in the error queue

/// Turn a C failure (-1 is returned) into a rust Result
pub(crate) fn cerr(t: libc::c_int) -> std::io::Result<libc::c_int> {
    match t {
        -1 => Err(std::io::Error::last_os_error()),
        _ => Ok(t),
    }
}

pub(crate) fn exceptional_condition_fd(
    socket_of_interest: &std::net::UdpSocket,
) -> std::io::Result<AsyncFd<RawFd>> {
    let fd = cerr(unsafe { libc::epoll_create1(0) })?;

    let mut event = libc::epoll_event {
        events: libc::EPOLLPRI as u32,
        u64: 0u64,
    };

    cerr(unsafe {
        libc::epoll_ctl(
            fd,
            libc::EPOLL_CTL_ADD,
            socket_of_interest.as_raw_fd(),
            &mut event,
        )
    })?;

    AsyncFd::new(fd)
}

Then to consume, we can await the exceptional condition fd to become readable:

async fn fetch_send_timestamp(&self, expected_counter: u32) -> io::Result<NtpTimestamp> {
    trace!("waiting for timestamp socket to become readable to fetch a send timestamp");
    loop {
        // Send timestamps are sent to the udp socket's error queue. Sadly, tokio does not
        // currently support awaiting whether there is something in the error queue
        // see https://github.com/tokio-rs/tokio/issues/4885.
        //
        // Therefore, we manually configure an extra file descriptor to listen for POLLPRI on
        // the main udp socket. This `exceptional_condition` file descriptor becomes readable
        // when there is something in the error queue.
        let mut guard = self.exceptional_condition.readable().await?;
        match guard.try_io(|_| fetch_send_timestamp_help(self.io.get_ref(), expected_counter)) {
            Ok(Ok(Some(send_timestamp))) => {
                return Ok(send_timestamp);
            }
            Ok(Ok(None)) => {
                continue;
            }
            Ok(Err(e)) => {
                warn!(error = debug(&e), "Error fetching timestamp");
                return Err(e);
            }
            Err(_would_block) => {
                trace!("timestamp blocked after becoming readable, retrying");
                continue;
            }
        }
    }
}

It would be really nice if we could off-load this complexity onto tokio. Until then, maybe this code is helpful to someone.

@folkertdev
Copy link
Contributor

It looks like mio recently added the PRIORITY interest https://docs.rs/mio/0.8.6/src/mio/interest.rs.html#49. When tokio updates its version of mio, tokio could expose this interest (for linux and android, anyway).

To me, that sounds like a really nice solution for this issue. Is there anything against exposing Interest::PRIORITY?

carllerche pushed a commit that referenced this issue Jun 6, 2023
Add support for epoll priority events. The commit adds `Interest::PRIORITY`, `ready`, and `ready_mut` functions to `AsyncFd`.

Closes #4885
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-feature-request Category: A feature request. M-io Module: tokio/io
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants