Eric Blake
2023-Aug-30 20:11 UTC
[Libguestfs] [libnbd PATCH 0/2] (Attempt to) fix Rust on BSD-based builds
I managed to get a build of the async Rust handle compiling on FreeBSD (although the cirrus CI appears to not actually run 'make check' on non-Linux machines, at least when run on my fork): https://gitlab.com/ebblake/libnbd/-/jobs/4985192286 However, I'd really like Tage's review on patch 2 to see if my Rust makes sense. Eric Blake (2): maint: Favor 4-space indent in .rs files rust: Use mio::poll instead of requiring epoll rust/Cargo.toml | 2 +- rust/src/async_handle.rs | 46 +++++++++++++++++++++++++--------------- .editorconfig | 4 ++++ 3 files changed, 34 insertions(+), 18 deletions(-) -- 2.41.0
Eric Blake
2023-Aug-30 20:11 UTC
[Libguestfs] [libnbd PATCH 1/2] maint: Favor 4-space indent in .rs files
Since rustfmt favors 4-space indent on .rs files, we should do likewise to reduce the churn when running rustfmt after editing in an emacs session set to honor editorconfig. Signed-off-by: Eric Blake <eblake at redhat.com> --- .editorconfig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.editorconfig b/.editorconfig index 86ef47d0..f5bc5604 100644 --- a/.editorconfig +++ b/.editorconfig @@ -21,6 +21,10 @@ indent_size = 4 indent_style = tab indent_size = 4 +[*.rs] +# Match rustfmt style +indent_size = 4 + [{Makefile,Makefile.in,Makefile.am,*.mk}] # Make requires tabs. indent_style = tab -- 2.41.0
Eric Blake
2023-Aug-30 20:11 UTC
[Libguestfs] [libnbd PATCH 2/2] rust: Use mio::poll instead of requiring epoll
CI shows our async handle fails to build on FreeBSD and MacOS (where epoll() is not available as a syscall, and therefore not available as a Rust crate). We can instead accomplish the same level-probing effects by doing a zero-timeout poll with mio (which defers under the hood to epoll on Linux, and kqueue on BSD). Fixes: 223a9965 ("rust: async: Create an async friendly handle type") CC: Tage Johansson <tage.j.lists at posteo.net> Signed-off-by: Eric Blake <eblake at redhat.com> --- rust/Cargo.toml | 2 +- rust/src/async_handle.rs | 46 +++++++++++++++++++++++++--------------- 2 files changed, 30 insertions(+), 18 deletions(-) diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 0879b34c..391c80e9 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -49,7 +49,7 @@ thiserror = "1.0.40" log = { version = "0.4.19", optional = true } libc = "0.2.147" tokio = { optional = true, version = "1.29.1", default-features = false, features = ["rt", "sync", "net"] } -epoll = "4.3.3" +mio = "0.8.0" [features] default = ["log", "tokio"] diff --git a/rust/src/async_handle.rs b/rust/src/async_handle.rs index ab28b910..35b1c0d2 100644 --- a/rust/src/async_handle.rs +++ b/rust/src/async_handle.rs @@ -35,9 +35,11 @@ use crate::sys; use crate::Handle; use crate::{Error, FatalErrorKind, Result}; use crate::{AIO_DIRECTION_BOTH, AIO_DIRECTION_READ, AIO_DIRECTION_WRITE}; -use epoll::Events; +use mio::unix::SourceFd; +use mio::{Events, Interest as MioInterest, Poll, Token}; use std::sync::Arc; use std::sync::Mutex; +use std::time::Duration; use tokio::io::{unix::AsyncFd, Interest, Ready as IoReady}; use tokio::sync::Notify; use tokio::task; @@ -176,12 +178,12 @@ async fn polling_task(handle_data: &HandleData) -> Result<(), FatalErrorKind> { } = handle_data; let fd = handle.aio_get_fd().map_err(Error::to_fatal)?; let tokio_fd = AsyncFd::new(fd)?; - let epfd = epoll::create(false)?; - epoll::ctl( - epfd, - epoll::ControlOptions::EPOLL_CTL_ADD, - fd, - epoll::Event::new(Events::EPOLLIN | Events::EPOLLOUT, 42), + let mut events = Events::with_capacity(1); + let mut poll = Poll::new()?; + poll.registry().register( + &mut SourceFd(&fd), + Token(0), + MioInterest::READABLE | MioInterest::WRITABLE, )?; // The following loop does approximately the following things: @@ -248,19 +250,29 @@ async fn polling_task(handle_data: &HandleData) -> Result<(), FatalErrorKind> { } drop(pending_cmds_lock); - // Use epoll to check the current read/write availability on the fd. + // Use mio poll to check the current read/write availability on the fd. // This is needed because Tokio supports only edge-triggered // notifications but Libnbd requires level-triggered notifications. - let mut revent = epoll::Event { data: 0, events: 0 }; // Setting timeout to 0 means that it will return immediately. - epoll::wait(epfd, 0, std::slice::from_mut(&mut revent))?; - let revents = Events::from_bits(revent.events).unwrap(); - if !revents.contains(Events::EPOLLIN) { - ready_guard.clear_ready_matching(IoReady::READABLE); - } - if !revents.contains(Events::EPOLLOUT) { - ready_guard.clear_ready_matching(IoReady::WRITABLE); - } + // mio states that it is OS-dependent on whether a single event + // can be both readable and writable, but we survive just fine + // if we only see one direction even when both are available. + match poll.poll(&mut events, Some(Duration::ZERO)) { + Ok(_) => { + for event in &events { + if !event.is_readable() { + ready_guard.clear_ready_matching(IoReady::READABLE); + } + if !event.is_writable() { + ready_guard.clear_ready_matching(IoReady::WRITABLE); + } + } + } + Err(_) => { + ready_guard.clear_ready_matching(IoReady::READABLE); + ready_guard.clear_ready_matching(IoReady::WRITABLE); + } + }; ready_guard.retain_ready(); } } -- 2.41.0