From f18fd97a64e0e8833463f3112b6c0840e5416856 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Wed, 26 Jan 2022 13:53:05 -0800 Subject: [PATCH 1/4] Use rustix instead of direct calls to libc. Use the [rustix] syscall wrapper crate to factor out error handling and unsafe system calls. This reduces the amount of unsafe code here, and is a step towards factoring it out entirely once [`AsFd`] is stabilized and can replace `AsRawFd` for these kinds of uses. This does require incrementing the minimum required Rust version to 1.48. Please feel free to decline this PR if you don't wish to take on these new requirements. [rustix]: https://crates.io/crates/rustix/ [`AsFd`]: https://doc.rust-lang.org/stable/std/os/unix/io/trait.AsFd.html --- .github/workflows/ci.yml | 2 +- Cargo.toml | 5 ++--- examples/linux-timerfd.rs | 8 +++++++- src/lib.rs | 22 ++++++++++------------ 4 files changed, 20 insertions(+), 17 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9977332..96c536d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -92,7 +92,7 @@ jobs: matrix: # When updating this, the reminder to update the minimum supported # Rust version in Cargo.toml. - rust: ['1.46'] + rust: ['1.48'] steps: - uses: actions/checkout@v3 - name: Install Rust diff --git a/Cargo.toml b/Cargo.toml index e747ebd..eb2b06f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ name = "async-io" version = "1.12.0" authors = ["Stjepan Glavina "] edition = "2018" -rust-version = "1.46" +rust-version = "1.48" description = "Async I/O and timers" license = "Apache-2.0 OR MIT" repository = "https://github.com/smol-rs/async-io" @@ -33,7 +33,7 @@ waker-fn = "1.1.0" autocfg = "1" [target."cfg(unix)".dependencies] -libc = "0.2.77" +rustix = { version = "0.36.0", features = ["fs"] } [target.'cfg(windows)'.dependencies] windows-sys = { version = "0.42", features = ["Win32_Networking_WinSock"] } @@ -49,7 +49,6 @@ tempfile = "3" [target.'cfg(target_os = "linux")'.dev-dependencies] inotify = { version = "0.10", default-features = false } -nix = { version = "0.25", default-features = false } timerfd = "1" [target.'cfg(windows)'.dev-dependencies] diff --git a/examples/linux-timerfd.rs b/examples/linux-timerfd.rs index e57053b..f2b5bac 100644 --- a/examples/linux-timerfd.rs +++ b/examples/linux-timerfd.rs @@ -14,6 +14,7 @@ fn main() -> std::io::Result<()> { use async_io::Async; use futures_lite::future; + use rustix::fd::BorrowedFd; use timerfd::{SetTimeFlags, TimerFd, TimerState}; /// Sleeps using an OS timer. @@ -24,7 +25,12 @@ fn main() -> std::io::Result<()> { // When the OS timer fires, a 64-bit integer can be read from it. Async::new(timer)? - .read_with(|t| nix::unistd::read(t.as_raw_fd(), &mut [0u8; 8]).map_err(io::Error::from)) + .read_with(|t| { + // Safety: Assume `as_raw_fd()` returns a valid fd; when `AsFd` + // is stabilized, we can remove this unsafe and simplify. + let fd = unsafe { BorrowedFd::borrow_raw(t.as_raw_fd()) }; + rustix::io::read(fd, &mut [0u8; 8]).map_err(io::Error::from) + }) .await?; Ok(()) } diff --git a/src/lib.rs b/src/lib.rs index 0579e19..913178e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -598,21 +598,19 @@ impl Async { /// # std::io::Result::Ok(()) }); /// ``` pub fn new(io: T) -> io::Result> { - let fd = io.as_raw_fd(); + let raw = io.as_raw_fd(); // Put the file descriptor in non-blocking mode. - unsafe { - let mut res = libc::fcntl(fd, libc::F_GETFL); - if res != -1 { - res = libc::fcntl(fd, libc::F_SETFL, res | libc::O_NONBLOCK); - } - if res == -1 { - return Err(io::Error::last_os_error()); - } - } + // + // Safety: We assume `as_raw_fd()` returns a valid fd. When + // `AsFd` is stabilized and `TimerFd` implements it, we can + // remove this unsafe and simplify this. + let fd = unsafe { rustix::fd::BorrowedFd::borrow_raw(raw) }; + let flags = rustix::fs::fcntl_getfl(fd)?; + rustix::fs::fcntl_setfl(fd, flags | rustix::fs::OFlags::NONBLOCK)?; Ok(Async { - source: Reactor::get().insert_io(fd)?, + source: Reactor::get().insert_io(raw)?, io: Some(io), }) } @@ -1895,7 +1893,7 @@ fn connect(addr: SockAddr, domain: Domain, protocol: Option) -> io::Re match socket.connect(&addr) { Ok(_) => {} #[cfg(unix)] - Err(err) if err.raw_os_error() == Some(libc::EINPROGRESS) => {} + Err(err) if err.raw_os_error() == Some(rustix::io::Errno::INPROGRESS.raw_os_error()) => {} Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} Err(err) => return Err(err), } From 9c148f4a9d4671f02bef609b89a99d1ec93f40e6 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Tue, 29 Nov 2022 09:57:29 -0800 Subject: [PATCH 2/4] Disable the use-libc-auxv feature. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index eb2b06f..f386ec8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,7 +33,7 @@ waker-fn = "1.1.0" autocfg = "1" [target."cfg(unix)".dependencies] -rustix = { version = "0.36.0", features = ["fs"] } +rustix = { version = "0.36.0", default-features = false, features = ["std", "fs"] } [target.'cfg(windows)'.dependencies] windows-sys = { version = "0.42", features = ["Win32_Networking_WinSock"] } From d156bdc464cb1d4a538b6c3fb389aa802ea807ca Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Tue, 29 Nov 2022 13:40:35 -0800 Subject: [PATCH 3/4] Use rustix to call `FIONBIO` on Windows. This eliminates the need for a direct dependency on windows-sys. --- Cargo.toml | 7 +------ src/lib.rs | 10 ++-------- 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f386ec8..1dfb5cf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,7 @@ futures-lite = "1.11.0" log = "0.4.11" parking = "2.0.0" polling = "2.0.0" +rustix = { version = "0.36.0", default-features = false, features = ["std", "fs"] } slab = "0.4.2" socket2 = { version = "0.4.2", features = ["all"] } waker-fn = "1.1.0" @@ -32,12 +33,6 @@ waker-fn = "1.1.0" [build-dependencies] autocfg = "1" -[target."cfg(unix)".dependencies] -rustix = { version = "0.36.0", default-features = false, features = ["std", "fs"] } - -[target.'cfg(windows)'.dependencies] -windows-sys = { version = "0.42", features = ["Win32_Networking_WinSock"] } - [dev-dependencies] async-channel = "1" async-net = "1" diff --git a/src/lib.rs b/src/lib.rs index 913178e..81b3fd6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -676,16 +676,10 @@ impl Async { /// ``` pub fn new(io: T) -> io::Result> { let sock = io.as_raw_socket(); + let borrowed = unsafe { rustix::fd::BorrowedFd::borrow_raw(sock) }; // Put the socket in non-blocking mode. - - use windows_sys::Win32::Networking::WinSock; - - let mut nonblocking = true as _; - let res = unsafe { WinSock::ioctlsocket(sock as _, WinSock::FIONBIO, &mut nonblocking) }; - if res != 0 { - return Err(io::Error::last_os_error()); - } + rustix::io::ioctl_fionbio(borrowed, true)?; Ok(Async { source: Reactor::get().insert_io(sock)?, From e8f1da9c0eb76ca81746da6ea327f79b1800fb00 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Tue, 29 Nov 2022 13:59:52 -0800 Subject: [PATCH 4/4] Comment that we need Rust >= 1.63 to use io_safety. --- examples/linux-timerfd.rs | 6 ++++-- src/lib.rs | 10 +++++++--- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/examples/linux-timerfd.rs b/examples/linux-timerfd.rs index f2b5bac..05ca38c 100644 --- a/examples/linux-timerfd.rs +++ b/examples/linux-timerfd.rs @@ -26,8 +26,10 @@ fn main() -> std::io::Result<()> { // When the OS timer fires, a 64-bit integer can be read from it. Async::new(timer)? .read_with(|t| { - // Safety: Assume `as_raw_fd()` returns a valid fd; when `AsFd` - // is stabilized, we can remove this unsafe and simplify. + // Safety: We assume `as_raw_fd()` returns a valid fd. When we + // can depend on Rust >= 1.63, where `AsFd` is stabilized, and + // when `TimerFd` implements it, we can remove this unsafe and + // simplify this. let fd = unsafe { BorrowedFd::borrow_raw(t.as_raw_fd()) }; rustix::io::read(fd, &mut [0u8; 8]).map_err(io::Error::from) }) diff --git a/src/lib.rs b/src/lib.rs index 81b3fd6..db6756b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -602,9 +602,9 @@ impl Async { // Put the file descriptor in non-blocking mode. // - // Safety: We assume `as_raw_fd()` returns a valid fd. When - // `AsFd` is stabilized and `TimerFd` implements it, we can - // remove this unsafe and simplify this. + // Safety: We assume `as_raw_fd()` returns a valid fd. When we can + // depend on Rust >= 1.63, where `AsFd` is stabilized, and when + // `TimerFd` implements it, we can remove this unsafe and simplify this. let fd = unsafe { rustix::fd::BorrowedFd::borrow_raw(raw) }; let flags = rustix::fs::fcntl_getfl(fd)?; rustix::fs::fcntl_setfl(fd, flags | rustix::fs::OFlags::NONBLOCK)?; @@ -679,6 +679,10 @@ impl Async { let borrowed = unsafe { rustix::fd::BorrowedFd::borrow_raw(sock) }; // Put the socket in non-blocking mode. + // + // Safety: We assume `as_raw_socket()` returns a valid fd. When we can + // depend on Rust >= 1.63, where `AsFd` is stabilized, and when + // `TimerFd` implements it, we can remove this unsafe and simplify this. rustix::io::ioctl_fionbio(borrowed, true)?; Ok(Async {