diff --git a/Cargo.toml b/Cargo.toml index 362d00c..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" @@ -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"))'.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 f8daf3e..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"))] +#[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")))] +#[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"))] + #[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"))] + #[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"))] + #[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 5531234..40f7cc9 100644 --- a/src/reaper/wait.rs +++ b/src/reaper/wait.rs @@ -219,6 +219,54 @@ cfg_if::cfg_if! { } } + /// Tell if we are able to use this backend. + 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"))] { + 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