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