From 8296d359c7c9cf06ba64c08713e731cf0aba5957 Mon Sep 17 00:00:00 2001 From: Roger Coll Date: Tue, 26 Nov 2024 19:52:52 +0100 Subject: [PATCH 1/5] feat: add freebsd Waitable child --- Cargo.toml | 4 ++-- src/reaper/mod.rs | 10 ++++----- src/reaper/wait.rs | 51 +++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 57 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6e61ef6..8730518 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ exclude = ["/.*"] [dependencies] async-lock = "3.0.0" -async-io = "2.3.0" +async-io = "2.4.0" cfg-if = "1.0" event-listener = "5.1.0" futures-lite = "2.0.0" @@ -26,7 +26,7 @@ tracing = { version = "0.1.40", default-features = false, optional = true } async-signal = "0.2.3" rustix = { version = "1.0", default-features = false, features = ["std", "fs", "process"] } -[target.'cfg(any(windows, target_os = "linux"))'.dependencies] +[target.'cfg(any(windows, target_os = "linux", target_os = "freebsd"))'.dependencies] async-channel = "2.0.0" async-task = "4.7.0" diff --git a/src/reaper/mod.rs b/src/reaper/mod.rs index f8daf3e..2baa7ea 100644 --- a/src/reaper/mod.rs +++ b/src/reaper/mod.rs @@ -12,13 +12,13 @@ #![allow(irrefutable_let_patterns)] /// Enable the waiting reaper. -#[cfg(any(windows, target_os = "linux"))] +#[cfg(any(windows, target_os = "linux", target_os = "freebsd"))] macro_rules! cfg_wait { ($($tt:tt)*) => {$($tt)*}; } /// Enable the waiting reaper. -#[cfg(not(any(windows, target_os = "linux")))] +#[cfg(not(any(windows, target_os = "linux", target_os = "freebsd")))] macro_rules! cfg_wait { ($($tt:tt)*) => {}; } @@ -48,7 +48,7 @@ use std::sync::Mutex; /// The underlying system reaper. pub(crate) enum Reaper { - #[cfg(any(windows, target_os = "linux"))] + #[cfg(any(windows, target_os = "linux", target_os = "freebsd"))] /// The reaper based on the wait backend. Wait(wait::Reaper), @@ -59,7 +59,7 @@ pub(crate) enum Reaper { /// The wrapper around a child. pub(crate) enum ChildGuard { - #[cfg(any(windows, target_os = "linux"))] + #[cfg(any(windows, target_os = "linux", target_os = "freebsd"))] /// The child guard based on the wait backend. Wait(wait::ChildGuard), @@ -70,7 +70,7 @@ pub(crate) enum ChildGuard { /// A lock on the reaper. pub(crate) enum Lock { - #[cfg(any(windows, target_os = "linux"))] + #[cfg(any(windows, target_os = "linux", target_os = "freebsd"))] /// The wait-based reaper needs no lock. Wait, diff --git a/src/reaper/wait.rs b/src/reaper/wait.rs index 5531234..c2f5b6b 100644 --- a/src/reaper/wait.rs +++ b/src/reaper/wait.rs @@ -134,7 +134,7 @@ impl ChildGuard { } cfg_if::cfg_if! { - if #[cfg(target_os = "linux")] { + if #[cfg(any(target_os = "linux"))] { use async_io::Async; use rustix::process; use std::os::unix::io::OwnedFd; @@ -219,9 +219,58 @@ cfg_if::cfg_if! { } } + /// Tell if we are able to use this backend. + pub(crate) fn available() -> bool { + true + } + } else if #[cfg(target_os = "freebsd")] { + use async_io::os::kqueue::{Exit, Filter}; + use std::num::NonZeroI32; + + /// Waitable version of `std::process::Child` + struct WaitableChild { + child: std::process::Child, + handle: Filter, + } + + impl WaitableChild { + fn new(child: std::process::Child) -> io::Result { + // std::process::Child id must provide a positive PID value + let exit_filter = unsafe { Filter::new(Exit::from_pid(NonZeroI32::new_unchecked( + child + .id() + .try_into() + .expect("could not transform pid to i32 type") + )))? + }; + + + Ok(Self { + handle: exit_filter, + child: child, + }) + } + + fn get_mut(&mut self) -> &mut std::process::Child { + &mut self.child + } + + fn poll_wait(&mut self, cx: &mut Context<'_>) -> Poll> { + loop { + if let Some(status) = self.child.try_wait()? { + return Poll::Ready(Ok(status)); + } + + // Wait for us to become readable. + futures_lite::ready!(self.handle.poll_ready(cx))?; + } + } + } + /// Tell if we are able to use this backend. pub(crate) fn available() -> bool { true } } + } From bb780bb8c7998a6594b07727dbaec6056405e8d2 Mon Sep 17 00:00:00 2001 From: Roger Coll Date: Tue, 26 Nov 2024 19:58:08 +0100 Subject: [PATCH 2/5] remove unused any cfg --- src/reaper/wait.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/reaper/wait.rs b/src/reaper/wait.rs index c2f5b6b..e5a2172 100644 --- a/src/reaper/wait.rs +++ b/src/reaper/wait.rs @@ -134,7 +134,7 @@ impl ChildGuard { } cfg_if::cfg_if! { - if #[cfg(any(target_os = "linux"))] { + if #[cfg(target_os = "linux")] { use async_io::Async; use rustix::process; use std::os::unix::io::OwnedFd; @@ -244,7 +244,6 @@ cfg_if::cfg_if! { )))? }; - Ok(Self { handle: exit_filter, child: child, @@ -272,5 +271,4 @@ cfg_if::cfg_if! { true } } - } From a803019d92a8a552223e53742f3abbab4f00c665 Mon Sep 17 00:00:00 2001 From: Roger Coll Date: Wed, 4 Dec 2024 15:56:57 +0100 Subject: [PATCH 3/5] chore: rustfmt waitable code --- src/reaper/wait.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/reaper/wait.rs b/src/reaper/wait.rs index e5a2172..56f890e 100644 --- a/src/reaper/wait.rs +++ b/src/reaper/wait.rs @@ -236,11 +236,12 @@ cfg_if::cfg_if! { impl WaitableChild { fn new(child: std::process::Child) -> io::Result { // std::process::Child id must provide a positive PID value - let exit_filter = unsafe { Filter::new(Exit::from_pid(NonZeroI32::new_unchecked( + let exit_filter = unsafe { + Filter::new(Exit::from_pid(NonZeroI32::new_unchecked( child - .id() - .try_into() - .expect("could not transform pid to i32 type") + .id() + .try_into() + .expect("could not transform pid to i32 type"), )))? }; From e2583f4efba16a537174da60530905e095b6bf66 Mon Sep 17 00:00:00 2001 From: Roger Coll Date: Wed, 20 Aug 2025 16:55:43 +0200 Subject: [PATCH 4/5] add support for all kqueue platforms --- Cargo.toml | 2 +- src/reaper/mod.rs | 50 +++++++++++++++++++++++++++++++++++++++++----- src/reaper/wait.rs | 2 +- 3 files changed, 47 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ddd87b5..6133683 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,7 +26,7 @@ async-lock = "3.0.0" async-signal = "0.2.3" rustix = { version = "1.0", default-features = false, features = ["std", "fs", "process"] } -[target.'cfg(any(windows, target_os = "linux", target_os = "freebsd"))'.dependencies] +[target.'cfg(any(windows, target_os = "linux", target_os = "freebsd", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd", target_os = "dragonfly", target_vendor = "apple"))'.dependencies] async-channel = "2.0.0" async-task = "4.7.0" diff --git a/src/reaper/mod.rs b/src/reaper/mod.rs index 2baa7ea..4d5791e 100644 --- a/src/reaper/mod.rs +++ b/src/reaper/mod.rs @@ -12,13 +12,29 @@ #![allow(irrefutable_let_patterns)] /// Enable the waiting reaper. -#[cfg(any(windows, target_os = "linux", target_os = "freebsd"))] +#[cfg(any( + windows, + target_os = "linux", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "dragonfly", + target_vendor = "apple" +))] macro_rules! cfg_wait { ($($tt:tt)*) => {$($tt)*}; } /// Enable the waiting reaper. -#[cfg(not(any(windows, target_os = "linux", target_os = "freebsd")))] +#[cfg(not(any( + windows, + target_os = "linux", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "dragonfly", + target_vendor = "apple" +)))] macro_rules! cfg_wait { ($($tt:tt)*) => {}; } @@ -48,7 +64,15 @@ use std::sync::Mutex; /// The underlying system reaper. pub(crate) enum Reaper { - #[cfg(any(windows, target_os = "linux", target_os = "freebsd"))] + #[cfg(any( + windows, + target_os = "linux", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "dragonfly", + target_vendor = "apple" + ))] /// The reaper based on the wait backend. Wait(wait::Reaper), @@ -59,7 +83,15 @@ pub(crate) enum Reaper { /// The wrapper around a child. pub(crate) enum ChildGuard { - #[cfg(any(windows, target_os = "linux", target_os = "freebsd"))] + #[cfg(any( + windows, + target_os = "linux", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "dragonfly", + target_vendor = "apple" + ))] /// The child guard based on the wait backend. Wait(wait::ChildGuard), @@ -70,7 +102,15 @@ pub(crate) enum ChildGuard { /// A lock on the reaper. pub(crate) enum Lock { - #[cfg(any(windows, target_os = "linux", target_os = "freebsd"))] + #[cfg(any( + windows, + target_os = "linux", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "dragonfly", + target_vendor = "apple" + ))] /// The wait-based reaper needs no lock. Wait, diff --git a/src/reaper/wait.rs b/src/reaper/wait.rs index 56f890e..9979829 100644 --- a/src/reaper/wait.rs +++ b/src/reaper/wait.rs @@ -223,7 +223,7 @@ cfg_if::cfg_if! { pub(crate) fn available() -> bool { true } - } else if #[cfg(target_os = "freebsd")] { + } else if #[cfg(any( target_vendor = "apple", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd", target_os = "dragonfly" ))] { use async_io::os::kqueue::{Exit, Filter}; use std::num::NonZeroI32; From 5728009ee01fe7d22b1cb30b99ca89270b8bf501 Mon Sep 17 00:00:00 2001 From: Roger Coll Date: Wed, 20 Aug 2025 17:07:46 +0200 Subject: [PATCH 5/5] bump async-io to 2.5.0 --- Cargo.toml | 2 +- src/reaper/wait.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6133683..0a49e52 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ categories = ["asynchronous", "os"] exclude = ["/.*"] [dependencies] -async-io = "2.3.0" +async-io = "2.5.0" cfg-if = "1.0" event-listener = "5.1.0" futures-lite = "2.0.0" diff --git a/src/reaper/wait.rs b/src/reaper/wait.rs index 9979829..40f7cc9 100644 --- a/src/reaper/wait.rs +++ b/src/reaper/wait.rs @@ -223,7 +223,7 @@ cfg_if::cfg_if! { pub(crate) fn available() -> bool { true } - } else if #[cfg(any( target_vendor = "apple", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd", target_os = "dragonfly" ))] { + } else if #[cfg(any(target_vendor = "apple", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd", target_os = "dragonfly"))] { use async_io::os::kqueue::{Exit, Filter}; use std::num::NonZeroI32;