diff --git a/Cargo.toml b/Cargo.toml index b4e2e26..a3c8bf0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ exclude = ["/.*"] [dependencies] async-lock = "3.0.0" -async-io = "2.1.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 } async-signal = "0.2.3" rustix = { version = "0.38", default-features = false, features = ["std", "fs"] } -[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 2a878cd..a821c9f 100644 --- a/src/reaper/wait.rs +++ b/src/reaper/wait.rs @@ -217,6 +217,54 @@ 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