From bb6fad51be3d12af1a46166b2a6d6f62a011d6b1 Mon Sep 17 00:00:00 2001 From: Simon Berger Date: Mon, 16 Feb 2026 14:12:20 +0100 Subject: [PATCH 1/2] chore: update nix to 0.31 --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 72d98bba6..121e0bcb6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,8 +21,8 @@ async-tokio = ["futures", "tokio", "mio-evented"] [dependencies] futures = { version = "0.3", optional = true } -nix = "0.26" -mio = { version = "1", optional = true, features = ["os-ext"]} +nix = { version = "0.31", features = ["event"] } +mio = { version = "1", optional = true, features = ["os-ext"] } tokio = { version = "1", optional = true, features = ["net"] } [dev-dependencies] From 3f1188308f69cf7cf56300533972988c2aac2fc1 Mon Sep 17 00:00:00 2001 From: Simon Berger Date: Mon, 16 Feb 2026 14:08:33 +0100 Subject: [PATCH 2/2] refactor: migrate to nix Epoll abstraction --- src/lib.rs | 55 +++++++++++++++++++++++------------------------------- 1 file changed, 23 insertions(+), 32 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 4a258d9ca..22bff1b02 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -53,8 +53,8 @@ use std::io; use std::io::prelude::*; #[cfg(any(target_os = "linux", target_os = "android", feature = "async-tokio"))] use std::io::SeekFrom; -#[cfg(not(target_os = "wasi"))] -use std::os::unix::prelude::*; +#[cfg(any(feature = "async-tokio", feature = "mio-evented"))] +use std::os::fd::{AsRawFd, RawFd}; use std::path::Path; use std::{fs, fs::File}; @@ -65,9 +65,7 @@ use mio::event::Source; #[cfg(feature = "mio-evented")] use mio::unix::SourceFd; #[cfg(any(target_os = "linux", target_os = "android"))] -use nix::sys::epoll::*; -#[cfg(not(target_os = "wasi"))] -use nix::unistd::close; +use nix::sys::epoll::{Epoll, EpollCreateFlags, EpollEvent, EpollFlags, EpollTimeout}; #[cfg(feature = "async-tokio")] use std::task::Poll; #[cfg(feature = "async-tokio")] @@ -514,7 +512,9 @@ fn extract_pin_fom_path_test() { #[derive(Debug)] pub struct PinPoller { pin_num: u64, - epoll_fd: RawFd, + #[cfg(any(target_os = "linux", target_os = "android"))] + epoll: Epoll, + #[cfg(any(target_os = "linux", target_os = "android"))] devfile: File, } #[cfg(not(target_os = "wasi"))] @@ -531,21 +531,16 @@ impl PinPoller { #[cfg(any(target_os = "linux", target_os = "android"))] pub fn new(pin_num: u64) -> Result { let devfile: File = File::open(format!("/sys/class/gpio/gpio{}/value", pin_num))?; - let devfile_fd = devfile.as_raw_fd(); - let epoll_fd = epoll_create()?; - let mut event = EpollEvent::new(EpollFlags::EPOLLPRI | EpollFlags::EPOLLET, 0u64); - - match epoll_ctl(epoll_fd, EpollOp::EpollCtlAdd, devfile_fd, &mut event) { - Ok(_) => Ok(PinPoller { - pin_num, - devfile, - epoll_fd, - }), - Err(err) => { - let _ = close(epoll_fd); // cleanup - Err(::std::convert::From::from(err)) - } - } + let epoll = Epoll::new(EpollCreateFlags::empty())?; + epoll.add( + &devfile, + EpollEvent::new(EpollFlags::EPOLLPRI | EpollFlags::EPOLLET, 0u64), + )?; + Ok(PinPoller { + pin_num, + devfile, + epoll, + }) } #[cfg(not(any(target_os = "linux", target_os = "android")))] @@ -575,7 +570,13 @@ impl PinPoller { flush_input_from_file(&mut self.devfile, 255)?; let dummy_event = EpollEvent::new(EpollFlags::EPOLLPRI | EpollFlags::EPOLLET, 0u64); let mut events: [EpollEvent; 1] = [dummy_event]; - let cnt = epoll_wait(self.epoll_fd, &mut events, timeout_ms)?; + // LOGIC: Previous versions essentially passed `timeout_ms as c_int` to the + // kernel without validation. We now validate that the value is in range + // -1..=i32::MAX. A future major version of this library should update + // the `poll` signature. + let timeout = EpollTimeout::try_from(timeout_ms as i128) + .map_err(|err| Error::Io(io::Error::other(err)))?; + let cnt = self.epoll.wait(&mut events, timeout)?; Ok(match cnt { 0 => None, // timeout _ => Some(get_value_from_file(&mut self.devfile)?), @@ -588,16 +589,6 @@ impl PinPoller { } } -#[cfg(not(target_os = "wasi"))] -impl Drop for PinPoller { - fn drop(&mut self) { - // we implement drop to close the underlying epoll fd as - // it does not implement drop itself. This is similar to - // how mio works - close(self.epoll_fd).unwrap(); // panic! if close files - } -} - #[cfg(feature = "mio-evented")] #[derive(Debug)] pub struct AsyncPinPoller {