diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index dcc3e54..bc26987 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -16,7 +16,7 @@ jobs: - windows toolchain: - stable - - 1.77.0 + - 1.86.0 features: - tokio1 - std diff --git a/Cargo.toml b/Cargo.toml index 94e543e..920483d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ readme = "README.md" edition = "2021" exclude = ["/bin", "/.github"] -rust-version = "1.77.0" +rust-version = "1.86.0" [dependencies] futures = { version = "0.3.30", optional = true } @@ -41,9 +41,6 @@ tokio = { version = "1.38.2", features = ["io-util", "macros", "process", "rt", [features] default = ["creation-flags", "job-object", "kill-on-drop", "process-group", "process-session", "tracing"] -## Enable Any trait bound on the ChildWrapper traits -downcasting = [] - ## Enable internal tracing logs tracing = ["dep:tracing"] diff --git a/README.md b/README.md index 1e540dd..cdeaa58 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ - **[API documentation][docs]**. - [Dual-licensed][copyright] with Apache 2.0 and MIT. - Successor to [command-group](https://github.com/watchexec/command-group). -- Minimum Supported Rust Version: 1.77.0. +- Minimum Supported Rust Version: 1.86.0. - Only the latest stable rustc version is supported. - MSRV increases will not incur major version bumps. diff --git a/src/generic_wrap.rs b/src/generic_wrap.rs index 6583c32..ce9c393 100644 --- a/src/generic_wrap.rs +++ b/src/generic_wrap.rs @@ -230,20 +230,4 @@ macro_rules! Wrap { }; } -macro_rules! MaybeAnyTrait { - ( - $(#[$($attrss:tt)*])* - $v:vis trait $name:ident $body:tt - ) => { - #[cfg(feature = "downcasting")] - $(#[$($attrss)*])* - $v trait $name: std::fmt::Debug + Send + Sync + std::any::Any $body - - #[cfg(not(feature = "downcasting"))] - $(#[$($attrss)*])* - $v trait $name: std::fmt::Debug + Send + Sync $body - } -} - -pub(crate) use MaybeAnyTrait; pub(crate) use Wrap; diff --git a/src/std.rs b/src/std.rs index 89d15d3..f3e30fd 100644 --- a/src/std.rs +++ b/src/std.rs @@ -9,7 +9,7 @@ //! ``` #[doc(inline)] -pub use core::{StdChild, StdChildWrapper, StdCommandWrap, StdCommandWrapper}; +pub use core::{StdChildWrapper, StdCommandWrap, StdCommandWrapper}; #[cfg(all(windows, feature = "creation-flags"))] #[doc(inline)] pub use creation_flags::CreationFlags; diff --git a/src/std/core.rs b/src/std/core.rs index e5ec4e6..86e0f59 100644 --- a/src/std/core.rs +++ b/src/std/core.rs @@ -1,4 +1,5 @@ use std::{ + any::Any, io::{Read, Result}, process::{Child, ChildStderr, ChildStdin, ChildStdout, Command, ExitStatus, Output}, }; @@ -15,22 +16,21 @@ crate::generic_wrap::Wrap!( StdCommandWrapper, Child, StdChildWrapper, - StdChild // |child| StdChild(child) + |child| child ); -crate::generic_wrap::MaybeAnyTrait! { /// Wrapper for `std::process::Child`. /// /// This trait exposes most of the functionality of the underlying [`Child`]. It is implemented for -/// [`StdChild`] (a thin wrapper around [`Child`]) (because implementing directly on [`Child`] would -/// loop) and by wrappers. +/// [`Child`] and by wrappers. /// /// The required methods are `inner`, `inner_mut`, and `into_inner`. That provides access to the -/// underlying `Child` and allows the wrapper to be dropped and the `Child` to be used directly if -/// necessary. +/// lower layer and ultimately allows the wrappers to be unwrap and the `Child` to be used directly +/// if necessary. There are convenience `inner_child`, `inner_child_mut` and `into_inner_child` +/// methods on the trait object. /// /// It also makes it possible for all the other methods to have default implementations. Some are -/// direct passthroughs to the underlying `Child`, while others are more complex. +/// direct passthroughs to the lower layers, while others are more complex. /// /// Here's a simple example of a wrapper: /// @@ -42,32 +42,32 @@ crate::generic_wrap::MaybeAnyTrait! { /// pub struct YourChildWrapper(Child); /// /// impl StdChildWrapper for YourChildWrapper { -/// fn inner(&self) -> &Child { +/// fn inner(&self) -> &dyn StdChildWrapper { /// &self.0 /// } /// -/// fn inner_mut(&mut self) -> &mut Child { +/// fn inner_mut(&mut self) -> &mut dyn StdChildWrapper { /// &mut self.0 /// } /// -/// fn into_inner(self: Box) -> Child { -/// (*self).0 +/// fn into_inner(self: Box) -> Box { +/// Box::new((*self).0) /// } /// } /// ``` -pub trait StdChildWrapper { - /// Obtain a reference to the underlying `Child`. - fn inner(&self) -> &Child; +pub trait StdChildWrapper: Any + std::fmt::Debug + Send { + /// Obtain a reference to the wrapped child. + fn inner(&self) -> &dyn StdChildWrapper; - /// Obtain a mutable reference to the underlying `Child`. - fn inner_mut(&mut self) -> &mut Child; + /// Obtain a mutable reference to the wrapped child. + fn inner_mut(&mut self) -> &mut dyn StdChildWrapper; - /// Consume the wrapper and return the underlying `Child`. + /// Consume the current wrapper and return the wrapped child. /// - /// Note that this may disrupt whatever the wrappers were doing. However, wrappers must ensure - /// that the `Child` is in a consistent state when this is called or they are dropped, so that - /// this is always safe. - fn into_inner(self: Box) -> Child; + /// Note that this may disrupt whatever the current wrapper was doing. However, wrappers must + /// ensure that the wrapped child is in a consistent state when this is called or they are + /// dropped, so that this is always safe. + fn into_inner(self: Box) -> Box; /// Obtain a clone if possible. /// @@ -79,23 +79,23 @@ pub trait StdChildWrapper { /// Obtain the `Child`'s stdin. /// - /// By default this is a passthrough to the underlying `Child`. + /// By default this is a passthrough to the wrapped child. fn stdin(&mut self) -> &mut Option { - &mut self.inner_mut().stdin + self.inner_mut().stdin() } /// Obtain the `Child`'s stdout. /// - /// By default this is a passthrough to the underlying `Child`. + /// By default this is a passthrough to the wrapped child. fn stdout(&mut self) -> &mut Option { - &mut self.inner_mut().stdout + self.inner_mut().stdout() } /// Obtain the `Child`'s stderr. /// - /// By default this is a passthrough to the underlying `Child`. + /// By default this is a passthrough to the wrapped child. fn stderr(&mut self) -> &mut Option { - &mut self.inner_mut().stderr + self.inner_mut().stderr() } /// Obtain the `Child`'s process ID. @@ -127,15 +127,7 @@ pub trait StdChildWrapper { /// library uses it to provide a consistent API across both std and Tokio (and because it's a /// generally useful API). fn start_kill(&mut self) -> Result<()> { - #[cfg(unix)] - { - self.signal(Signal::SIGKILL as _) - } - - #[cfg(not(unix))] - { - self.inner_mut().kill() - } + self.inner_mut().start_kill() } /// Check if the `Child` has exited without blocking, and if so, return its exit status. @@ -204,6 +196,51 @@ pub trait StdChildWrapper { /// was introduced by command-group to abstract over the signal behaviour between process groups /// and unwrapped processes. #[cfg(unix)] + fn signal(&self, sig: i32) -> Result<()> { + self.inner().signal(sig) + } +} + +impl StdChildWrapper for Child { + fn inner(&self) -> &dyn StdChildWrapper { + self + } + fn inner_mut(&mut self) -> &mut dyn StdChildWrapper { + self + } + fn into_inner(self: Box) -> Box { + self + } + fn stdin(&mut self) -> &mut Option { + &mut self.stdin + } + fn stdout(&mut self) -> &mut Option { + &mut self.stdout + } + fn stderr(&mut self) -> &mut Option { + &mut self.stderr + } + fn id(&self) -> u32 { + Child::id(self) + } + fn start_kill(&mut self) -> Result<()> { + #[cfg(unix)] + { + self.signal(Signal::SIGKILL as _) + } + + #[cfg(not(unix))] + { + Child::kill(self) + } + } + fn try_wait(&mut self) -> Result> { + Child::try_wait(self) + } + fn wait(&mut self) -> Result { + Child::wait(self) + } + #[cfg(unix)] fn signal(&self, sig: i32) -> Result<()> { kill( Pid::from_raw(i32::try_from(self.id()).map_err(std::io::Error::other)?), @@ -212,25 +249,51 @@ pub trait StdChildWrapper { .map_err(std::io::Error::from) } } -} -/// A thin wrapper around [`Child`]. -/// -/// This is used only because implementing [`StdChildWrapper`] directly on std's [`Child`] creates -/// loops in the type system. It is not intended to be used directly, but only to be used internally -/// by the library. -#[derive(Debug)] -pub struct StdChild(pub Child); - -impl StdChildWrapper for StdChild { - fn inner(&self) -> &Child { - &self.0 +impl dyn StdChildWrapper { + fn downcast_ref(&self) -> Option<&T> { + (self as &dyn Any).downcast_ref() } - fn inner_mut(&mut self) -> &mut Child { - &mut self.0 + + fn is_raw_child(&self) -> bool { + self.downcast_ref::().is_some() + } + + /// Obtain a reference to the underlying [`Child`]. + pub fn inner_child(&self) -> &Child { + let mut inner = self; + while !inner.is_raw_child() { + inner = inner.inner(); + } + + // UNWRAP: we've just checked that it's Some with is_raw_child() + inner.downcast_ref().unwrap() + } + + /// Obtain a mutable reference to the underlying [`Child`]. + /// + /// Modifying the raw child may be unsound depending on the layering of wrappers. + pub unsafe fn inner_child_mut(&mut self) -> &mut Child { + let mut inner = self; + while !inner.is_raw_child() { + inner = inner.inner_mut(); + } + + // UNWRAP: we've just checked that with is_raw_child() + (inner as &mut dyn Any).downcast_mut().unwrap() } - fn into_inner(self: Box) -> Child { - (*self).0 + + /// Obtain the underlying [`Child`]. + /// + /// Unwrapping everything may be unsound depending on the state of the wrappers. + pub unsafe fn into_inner_child(self: Box) -> Child { + let mut inner = self; + while !inner.is_raw_child() { + inner = inner.into_inner(); + } + + // UNWRAP: we've just checked that with is_raw_child() + *(inner as Box).downcast().unwrap() } } diff --git a/src/std/job_object.rs b/src/std/job_object.rs index 574de65..f557e97 100644 --- a/src/std/job_object.rs +++ b/src/std/job_object.rs @@ -1,7 +1,7 @@ use std::{ io::Result, os::windows::{io::AsRawHandle, process::CommandExt}, - process::{Child, Command, ExitStatus}, + process::{Command, ExitStatus}, time::Duration, }; @@ -66,7 +66,7 @@ impl StdCommandWrapper for JobObject { #[cfg(feature = "tracing")] debug!(?create_suspended, "options from other wrappers"); - let handle = HANDLE(inner.inner().as_raw_handle() as _); + let handle = HANDLE(inner.inner_child().as_raw_handle() as _); let job_port = make_job_object(handle, false)?; @@ -99,13 +99,13 @@ impl JobObjectChild { } impl StdChildWrapper for JobObjectChild { - fn inner(&self) -> &Child { + fn inner(&self) -> &dyn StdChildWrapper { self.inner.inner() } - fn inner_mut(&mut self) -> &mut Child { + fn inner_mut(&mut self) -> &mut dyn StdChildWrapper { self.inner.inner_mut() } - fn into_inner(self: Box) -> Child { + fn into_inner(self: Box) -> Box { // manually drop the completion port let its = std::mem::ManuallyDrop::new(self.job_port); unsafe { CloseHandle(its.completion_port.0) }.ok(); @@ -135,13 +135,13 @@ impl StdChildWrapper for JobObjectChild { let JobPort { completion_port, .. } = self.job_port; - wait_on_job(completion_port, None)?; + let _ = wait_on_job(completion_port, None)?; Ok(status) } #[cfg_attr(feature = "tracing", instrument(level = "debug", skip(self)))] fn try_wait(&mut self) -> Result> { - wait_on_job(self.job_port.completion_port, Some(Duration::ZERO))?; + let _ = wait_on_job(self.job_port.completion_port, Some(Duration::ZERO))?; self.inner.try_wait() } } diff --git a/src/std/process_group.rs b/src/std/process_group.rs index ff94518..d34356d 100644 --- a/src/std/process_group.rs +++ b/src/std/process_group.rs @@ -2,7 +2,7 @@ use std::{ io::{Error, Result}, ops::ControlFlow, os::unix::process::{CommandExt, ExitStatusExt}, - process::{Child, Command, ExitStatus}, + process::{Command, ExitStatus}, }; use nix::{ @@ -150,13 +150,13 @@ impl ProcessGroupChild { } impl StdChildWrapper for ProcessGroupChild { - fn inner(&self) -> &Child { + fn inner(&self) -> &dyn StdChildWrapper { self.inner.inner() } - fn inner_mut(&mut self) -> &mut Child { + fn inner_mut(&mut self) -> &mut dyn StdChildWrapper { self.inner.inner_mut() } - fn into_inner(self: Box) -> Child { + fn into_inner(self: Box) -> Box { self.inner.into_inner() } @@ -177,7 +177,7 @@ impl StdChildWrapper for ProcessGroupChild { self.exit_status = ChildExitStatus::Exited(status); // nevertheless, now wait and make sure we reap all children. - Self::wait_imp(self.pgid, WaitPidFlag::empty())?; + let _ = Self::wait_imp(self.pgid, WaitPidFlag::empty())?; Ok(status) } diff --git a/src/tokio/core.rs b/src/tokio/core.rs index 4806693..e3d232b 100644 --- a/src/tokio/core.rs +++ b/src/tokio/core.rs @@ -1,6 +1,8 @@ use std::{ + any::Any, future::Future, io::Result, + pin::Pin, process::{ExitStatus, Output}, }; @@ -24,7 +26,6 @@ crate::generic_wrap::Wrap!( |child| child ); -crate::generic_wrap::MaybeAnyTrait! { /// Wrapper for `tokio::process::Child`. /// /// This trait exposes most of the functionality of the underlying [`Child`]. It is implemented for @@ -47,32 +48,32 @@ crate::generic_wrap::MaybeAnyTrait! { /// pub struct YourChildWrapper(Child); /// /// impl TokioChildWrapper for YourChildWrapper { -/// fn inner(&self) -> &Child { +/// fn inner(&self) -> &dyn TokioChildWrapper { /// &self.0 /// } /// -/// fn inner_mut(&mut self) -> &mut Child { +/// fn inner_mut(&mut self) -> &mut dyn TokioChildWrapper { /// &mut self.0 /// } /// -/// fn into_inner(self: Box) -> Child { -/// (*self).0 +/// fn into_inner(self: Box) -> Box { +/// Box::new((*self).0) /// } /// } /// ``` -pub trait TokioChildWrapper { - /// Obtain a reference to the underlying `Child`. - fn inner(&self) -> &Child; +pub trait TokioChildWrapper: Any + std::fmt::Debug + Send { + /// Obtain a reference to the wrapped child. + fn inner(&self) -> &dyn TokioChildWrapper; - /// Obtain a mutable reference to the underlying `Child`. - fn inner_mut(&mut self) -> &mut Child; + /// Obtain a mutable reference to the wrapped child. + fn inner_mut(&mut self) -> &mut dyn TokioChildWrapper; - /// Consume the wrapper and return the underlying `Child`. + /// Consume the current wrapper and return the wrapped child. /// - /// Note that this may disrupt whatever the wrappers were doing. However, wrappers must ensure - /// that the `Child` is in a consistent state when this is called or they are dropped, so that - /// this is always safe. - fn into_inner(self: Box) -> Child; + /// Note that this may disrupt whatever the current wrapper was doing. However, wrappers must + /// ensure that the wrapped child is in a consistent state when this is called or they are + /// dropped, so that this is always safe. + fn into_inner(self: Box) -> Box; /// Obtain a clone if possible. /// @@ -84,23 +85,23 @@ pub trait TokioChildWrapper { /// Obtain the `Child`'s stdin. /// - /// By default this is a passthrough to the underlying `Child`. + /// By default this is a passthrough to the wrapped child. fn stdin(&mut self) -> &mut Option { - &mut self.inner_mut().stdin + self.inner_mut().stdin() } /// Obtain the `Child`'s stdout. /// - /// By default this is a passthrough to the underlying `Child`. + /// By default this is a passthrough to the wrapped child. fn stdout(&mut self) -> &mut Option { - &mut self.inner_mut().stdout + self.inner_mut().stdout() } /// Obtain the `Child`'s stderr. /// - /// By default this is a passthrough to the underlying `Child`. + /// By default this is a passthrough to the wrapped child. fn stderr(&mut self) -> &mut Option { - &mut self.inner_mut().stderr + self.inner_mut().stderr() } /// Obtain the `Child`'s process ID. @@ -121,7 +122,7 @@ pub trait TokioChildWrapper { fn kill(&mut self) -> Box> + Send + '_> { Box::new(async { self.start_kill()?; - Box::into_pin(self.wait()).await?; + self.wait().await?; Ok(()) }) } @@ -151,8 +152,8 @@ pub trait TokioChildWrapper { /// has exited will always return the same result. /// /// By default this is a passthrough to the underlying `Child`. - fn wait(&mut self) -> Box> + Send + '_> { - Box::new(self.inner_mut().wait()) + fn wait(&mut self) -> Pin> + Send + '_>> { + Box::pin(self.inner_mut().wait()) } /// Wait for the `Child` to exit and return its exit status and outputs. @@ -180,8 +181,7 @@ pub trait TokioChildWrapper { let stdout_fut = read_to_end(&mut stdout_pipe); let stderr_fut = read_to_end(&mut stderr_pipe); - let (status, stdout, stderr) = - try_join3(Box::into_pin(self.wait()), stdout_fut, stderr_fut).await?; + let (status, stdout, stderr) = try_join3(self.wait(), stdout_fut, stderr_fut).await?; // Drop happens after `try_join` due to drop(stdout_pipe); @@ -201,6 +201,43 @@ pub trait TokioChildWrapper { /// was introduced by command-group to abstract over the signal behaviour between process groups /// and unwrapped processes. #[cfg(unix)] + fn signal(&self, sig: i32) -> Result<()> { + self.inner().signal(sig) + } +} + +impl TokioChildWrapper for Child { + fn inner(&self) -> &dyn TokioChildWrapper { + self + } + fn inner_mut(&mut self) -> &mut dyn TokioChildWrapper { + self + } + fn into_inner(self: Box) -> Box { + Box::new(*self) + } + fn stdin(&mut self) -> &mut Option { + &mut self.stdin + } + fn stdout(&mut self) -> &mut Option { + &mut self.stdout + } + fn stderr(&mut self) -> &mut Option { + &mut self.stderr + } + fn id(&self) -> Option { + Child::id(self) + } + fn start_kill(&mut self) -> Result<()> { + Child::start_kill(self) + } + fn try_wait(&mut self) -> Result> { + Child::try_wait(self) + } + fn wait(&mut self) -> Pin> + Send + '_>> { + Box::pin(Child::wait(self)) + } + #[cfg(unix)] fn signal(&self, sig: i32) -> Result<()> { if let Some(id) = self.id() { kill( @@ -213,16 +250,50 @@ pub trait TokioChildWrapper { } } } -} -impl TokioChildWrapper for Child { - fn inner(&self) -> &Child { - self +impl dyn TokioChildWrapper { + fn downcast_ref(&self) -> Option<&T> { + (self as &dyn Any).downcast_ref() } - fn inner_mut(&mut self) -> &mut Child { - self + + fn is_raw_child(&self) -> bool { + self.downcast_ref::().is_some() + } + + /// Obtain a reference to the underlying [`Child`]. + pub fn inner_child(&self) -> &Child { + let mut inner = self; + while !inner.is_raw_child() { + inner = inner.inner(); + } + + // UNWRAP: we've just checked that it's Some with is_raw_child() + inner.downcast_ref().unwrap() } - fn into_inner(self: Box) -> Child { - *self + + /// Obtain a mutable reference to the underlying [`Child`]. + /// + /// Modifying the raw child may be unsound depending on the layering of wrappers. + pub unsafe fn inner_child_mut(&mut self) -> &mut Child { + let mut inner = self; + while !inner.is_raw_child() { + inner = inner.inner_mut(); + } + + // UNWRAP: we've just checked that with is_raw_child() + (inner as &mut dyn Any).downcast_mut().unwrap() + } + + /// Obtain the underlying [`Child`]. + /// + /// Unwrapping everything may be unsound depending on the state of the wrappers. + pub unsafe fn into_inner_child(self: Box) -> Child { + let mut inner = self; + while !inner.is_raw_child() { + inner = inner.into_inner(); + } + + // UNWRAP: we've just checked that with is_raw_child() + *(inner as Box).downcast().unwrap() } } diff --git a/src/tokio/job_object.rs b/src/tokio/job_object.rs index ca7c804..a5cf64a 100644 --- a/src/tokio/job_object.rs +++ b/src/tokio/job_object.rs @@ -1,9 +1,6 @@ -use std::{future::Future, io::Result, process::ExitStatus, time::Duration}; +use std::{future::Future, io::Result, pin::Pin, process::ExitStatus, time::Duration}; -use tokio::{ - process::{Child, Command}, - task::spawn_blocking, -}; +use tokio::{process::Command, task::spawn_blocking}; #[cfg(feature = "tracing")] use tracing::{debug, instrument}; use windows::Win32::{ @@ -78,7 +75,7 @@ impl TokioCommandWrapper for JobObject { let handle = HANDLE( inner - .inner() + .inner_child() .raw_handle() .expect("child has exited but it has not even started") as _, ); @@ -114,13 +111,13 @@ impl JobObjectChild { } impl TokioChildWrapper for JobObjectChild { - fn inner(&self) -> &Child { + fn inner(&self) -> &dyn TokioChildWrapper { self.inner.inner() } - fn inner_mut(&mut self) -> &mut Child { + fn inner_mut(&mut self) -> &mut dyn TokioChildWrapper { self.inner.inner_mut() } - fn into_inner(self: Box) -> Child { + fn into_inner(self: Box) -> Box { // manually drop the completion port let its = std::mem::ManuallyDrop::new(self.job_port); unsafe { CloseHandle(its.completion_port.0) }.ok(); @@ -136,8 +133,8 @@ impl TokioChildWrapper for JobObjectChild { } #[cfg_attr(feature = "tracing", instrument(level = "debug", skip(self)))] - fn wait(&mut self) -> Box> + Send + '_> { - Box::new(async { + fn wait(&mut self) -> Pin> + Send + '_>> { + Box::pin(async { if let ChildExitStatus::Exited(status) = &self.exit_status { return Ok(*status); } @@ -146,7 +143,7 @@ impl TokioChildWrapper for JobObjectChild { // always wait for parent to exit first, as by the time it does, // it's likely that all its children have already exited. - let status = Box::into_pin(self.inner.wait()).await?; + let status = self.inner.wait().await?; self.exit_status = ChildExitStatus::Exited(status); // nevertheless, now try reaping all children a few times... @@ -161,14 +158,14 @@ impl TokioChildWrapper for JobObjectChild { let JobPort { completion_port, .. } = self.job_port; - spawn_blocking(move || wait_on_job(completion_port, None)).await??; + let _ = spawn_blocking(move || wait_on_job(completion_port, None)).await??; Ok(status) }) } #[cfg_attr(feature = "tracing", instrument(level = "debug", skip(self)))] fn try_wait(&mut self) -> Result> { - wait_on_job(self.job_port.completion_port, Some(Duration::ZERO))?; + let _ = wait_on_job(self.job_port.completion_port, Some(Duration::ZERO))?; self.inner.try_wait() } } diff --git a/src/tokio/process_group.rs b/src/tokio/process_group.rs index f01a9e2..6b45255 100644 --- a/src/tokio/process_group.rs +++ b/src/tokio/process_group.rs @@ -3,6 +3,7 @@ use std::{ io::{Error, Result}, ops::ControlFlow, os::unix::process::ExitStatusExt, + pin::Pin, process::ExitStatus, }; @@ -15,10 +16,7 @@ use nix::{ }, unistd::Pid, }; -use tokio::{ - process::{Child, Command}, - task::spawn_blocking, -}; +use tokio::{process::Command, task::spawn_blocking}; #[cfg(feature = "tracing")] use tracing::instrument; @@ -162,13 +160,13 @@ impl ProcessGroupChild { } impl TokioChildWrapper for ProcessGroupChild { - fn inner(&self) -> &Child { + fn inner(&self) -> &dyn TokioChildWrapper { self.inner.inner() } - fn inner_mut(&mut self) -> &mut Child { + fn inner_mut(&mut self) -> &mut dyn TokioChildWrapper { self.inner.inner_mut() } - fn into_inner(self: Box) -> Child { + fn into_inner(self: Box) -> Box { self.inner.into_inner() } @@ -178,8 +176,8 @@ impl TokioChildWrapper for ProcessGroupChild { } #[cfg_attr(feature = "tracing", instrument(level = "debug", skip(self)))] - fn wait(&mut self) -> Box> + Send + '_> { - Box::new(async { + fn wait(&mut self) -> Pin> + Send + '_>> { + Box::pin(async { if let ChildExitStatus::Exited(status) = &self.exit_status { return Ok(*status); } @@ -189,7 +187,7 @@ impl TokioChildWrapper for ProcessGroupChild { // always wait for parent to exit first, as by the time it does, // it's likely that all its children have already been reaped. - let status = Box::into_pin(self.inner.wait()).await?; + let status = self.inner.wait().await?; self.exit_status = ChildExitStatus::Exited(status); // nevertheless, now try reaping all children a few times... @@ -201,7 +199,7 @@ impl TokioChildWrapper for ProcessGroupChild { // ...finally, if there are some that are still alive, // block in the background to reap them fully. - spawn_blocking(move || Self::wait_imp(pgid, WaitPidFlag::empty())).await??; + let _ = spawn_blocking(move || Self::wait_imp(pgid, WaitPidFlag::empty())).await??; Ok(status) }) } diff --git a/tests/std_unix/into_inner_write_stdin.rs b/tests/std_unix/into_inner_write_stdin.rs index 79a430b..d133e12 100644 --- a/tests/std_unix/into_inner_write_stdin.rs +++ b/tests/std_unix/into_inner_write_stdin.rs @@ -2,11 +2,13 @@ use super::prelude::*; #[test] fn nowrap() -> Result<()> { - let mut child = StdCommandWrap::with_new("cat", |command| { - command.stdin(Stdio::piped()).stdout(Stdio::piped()); - }) - .spawn()? - .into_inner(); + let mut child = unsafe { + StdCommandWrap::with_new("cat", |command| { + command.stdin(Stdio::piped()).stdout(Stdio::piped()); + }) + .spawn()? + .into_inner_child() + }; if let Some(mut din) = child.stdin.take() { din.write_all(b"hello")?; @@ -23,12 +25,14 @@ fn nowrap() -> Result<()> { #[test] fn process_group() -> Result<()> { - let mut child = StdCommandWrap::with_new("cat", |command| { - command.stdin(Stdio::piped()).stdout(Stdio::piped()); - }) - .wrap(ProcessGroup::leader()) - .spawn()? - .into_inner(); + let mut child = unsafe { + StdCommandWrap::with_new("cat", |command| { + command.stdin(Stdio::piped()).stdout(Stdio::piped()); + }) + .wrap(ProcessGroup::leader()) + .spawn()? + .into_inner_child() + }; if let Some(mut din) = child.stdin.take() { din.write_all(b"hello")?; @@ -45,12 +49,14 @@ fn process_group() -> Result<()> { #[test] fn process_session() -> Result<()> { - let mut child = StdCommandWrap::with_new("cat", |command| { - command.stdin(Stdio::piped()).stdout(Stdio::piped()); - }) - .wrap(ProcessSession) - .spawn()? - .into_inner(); + let mut child = unsafe { + StdCommandWrap::with_new("cat", |command| { + command.stdin(Stdio::piped()).stdout(Stdio::piped()); + }) + .wrap(ProcessSession) + .spawn()? + .into_inner_child() + }; if let Some(mut din) = child.stdin.take() { din.write_all(b"hello")?; diff --git a/tests/std_windows/into_inner_write_stdin.rs b/tests/std_windows/into_inner_write_stdin.rs index ff7ade9..d19f74d 100644 --- a/tests/std_windows/into_inner_write_stdin.rs +++ b/tests/std_windows/into_inner_write_stdin.rs @@ -11,12 +11,12 @@ fn nowrap() -> Result<()> { .spawn()? .into_inner(); - if let Some(mut din) = child.stdin.take() { + if let Some(mut din) = child.stdin().take() { din.write_all(b"hello")?; } let mut output = String::new(); - if let Some(mut out) = child.stdout.take() { + if let Some(mut out) = child.stdout().take() { out.read_to_string(&mut output)?; } @@ -36,12 +36,12 @@ fn job_object() -> Result<()> { .spawn()? .into_inner(); - if let Some(mut din) = child.stdin.take() { + if let Some(mut din) = child.stdin().take() { din.write_all(b"hello")?; } let mut output = String::new(); - if let Some(mut out) = child.stdout.take() { + if let Some(mut out) = child.stdout().take() { out.read_to_string(&mut output)?; } diff --git a/tests/std_windows/kill_and_try_wait.rs b/tests/std_windows/kill_and_try_wait.rs index 805bede..a8b8c67 100644 --- a/tests/std_windows/kill_and_try_wait.rs +++ b/tests/std_windows/kill_and_try_wait.rs @@ -8,7 +8,7 @@ fn nowrap() -> Result<()> { .spawn()?; assert!(child.try_wait()?.is_none(), "pre kill"); - (child.kill())?; + child.kill()?; sleep(DIE_TIME); assert!(child.try_wait()?.is_some(), "try_wait() one"); @@ -27,7 +27,7 @@ fn job_object() -> Result<()> { .spawn()?; assert!(child.try_wait()?.is_none(), "pre kill"); - (child.kill())?; + child.kill()?; sleep(DIE_TIME); assert!(child.try_wait()?.is_some(), "try_wait() one"); diff --git a/tests/tokio_unix/kill_and_try_wait.rs b/tests/tokio_unix/kill_and_try_wait.rs index fce83ba..2c25466 100644 --- a/tests/tokio_unix/kill_and_try_wait.rs +++ b/tests/tokio_unix/kill_and_try_wait.rs @@ -29,7 +29,7 @@ async fn process_group() -> Result<()> { Box::into_pin(child.kill()).await?; - let status = Box::into_pin(child.wait()).await?; + let status = child.wait().await?; assert!(!status.success()); sleep(DIE_TIME).await; diff --git a/tests/tokio_unix/multiproc_linux.rs b/tests/tokio_unix/multiproc_linux.rs index 89dc029..fa41499 100644 --- a/tests/tokio_unix/multiproc_linux.rs +++ b/tests/tokio_unix/multiproc_linux.rs @@ -83,7 +83,7 @@ async fn process_group_kill_group() -> Result<()> { nix::sys::signal::killpg(nix::unistd::Pid::from_raw(parent), Signal::SIGKILL).unwrap(); sleep(DIE_TIME).await; assert!(!pid_alive(child), "child process should be dead"); - Box::into_pin(leader.wait()).await.unwrap(); + leader.wait().await.unwrap(); assert!(!pid_alive(parent), "parent process should be dead"); Ok(()) @@ -170,7 +170,7 @@ async fn process_session_kill_group() -> Result<()> { nix::sys::signal::killpg(nix::unistd::Pid::from_raw(parent), Signal::SIGKILL).unwrap(); sleep(DIE_TIME).await; assert!(!pid_alive(child), "child process should be dead"); - Box::into_pin(leader.wait()).await.unwrap(); + leader.wait().await.unwrap(); assert!(!pid_alive(parent), "parent process should be dead"); Ok(()) diff --git a/tests/tokio_unix/wait_after_die.rs b/tests/tokio_unix/wait_after_die.rs index c197cdf..f1c85f7 100644 --- a/tests/tokio_unix/wait_after_die.rs +++ b/tests/tokio_unix/wait_after_die.rs @@ -8,7 +8,7 @@ async fn nowrap() -> Result<()> { .spawn()?; sleep(DIE_TIME).await; - let status = Box::into_pin(child.wait()).await?; + let status = child.wait().await?; assert!(status.success()); Ok(()) @@ -23,7 +23,7 @@ async fn process_group() -> Result<()> { .spawn()?; sleep(DIE_TIME).await; - let status = Box::into_pin(child.wait()).await?; + let status = child.wait().await?; assert!(status.success()); Ok(()) @@ -38,7 +38,7 @@ async fn process_session() -> Result<()> { .spawn()?; sleep(DIE_TIME).await; - let status = Box::into_pin(child.wait()).await?; + let status = child.wait().await?; assert!(status.success()); Ok(()) diff --git a/tests/tokio_unix/wait_twice.rs b/tests/tokio_unix/wait_twice.rs index 40461f2..3560cb4 100644 --- a/tests/tokio_unix/wait_twice.rs +++ b/tests/tokio_unix/wait_twice.rs @@ -7,10 +7,10 @@ async fn nowrap() -> Result<()> { }) .spawn()?; - let status = Box::into_pin(child.wait()).await?; + let status = child.wait().await?; assert!(status.success()); - let status = Box::into_pin(child.wait()).await?; + let status = child.wait().await?; assert!(status.success()); Ok(()) @@ -24,10 +24,10 @@ async fn process_group() -> Result<()> { .wrap(ProcessGroup::leader()) .spawn()?; - let status = Box::into_pin(child.wait()).await?; + let status = child.wait().await?; assert!(status.success()); - let status = Box::into_pin(child.wait()).await?; + let status = child.wait().await?; assert!(status.success()); Ok(()) @@ -41,10 +41,10 @@ async fn process_session() -> Result<()> { .wrap(ProcessSession) .spawn()?; - let status = Box::into_pin(child.wait()).await?; + let status = child.wait().await?; assert!(status.success()); - let status = Box::into_pin(child.wait()).await?; + let status = child.wait().await?; assert!(status.success()); Ok(()) diff --git a/tests/tokio_unix/wait_twice_after_sigterm.rs b/tests/tokio_unix/wait_twice_after_sigterm.rs index 2b2e7d7..e72b4a9 100644 --- a/tests/tokio_unix/wait_twice_after_sigterm.rs +++ b/tests/tokio_unix/wait_twice_after_sigterm.rs @@ -10,10 +10,10 @@ async fn nowrap() -> Result<()> { child.signal(Signal::SIGTERM as _)?; - let status = Box::into_pin(child.wait()).await?; + let status = child.wait().await?; assert_eq!(status.signal(), Some(Signal::SIGTERM as i32), "wait() one"); - let status = Box::into_pin(child.wait()).await?; + let status = child.wait().await?; assert_eq!(status.signal(), Some(Signal::SIGTERM as i32), "wait() two"); Ok(()) @@ -30,10 +30,10 @@ async fn process_group() -> Result<()> { child.signal(Signal::SIGTERM as _)?; - let status = Box::into_pin(child.wait()).await?; + let status = child.wait().await?; assert_eq!(status.signal(), Some(Signal::SIGTERM as i32), "wait() one"); - let status = Box::into_pin(child.wait()).await?; + let status = child.wait().await?; assert_eq!(status.signal(), Some(Signal::SIGTERM as i32), "wait() two"); Ok(()) @@ -50,10 +50,10 @@ async fn process_session() -> Result<()> { child.signal(Signal::SIGTERM as _)?; - let status = Box::into_pin(child.wait()).await?; + let status = child.wait().await?; assert_eq!(status.signal(), Some(Signal::SIGTERM as i32), "wait() one"); - let status = Box::into_pin(child.wait()).await?; + let status = child.wait().await?; assert_eq!(status.signal(), Some(Signal::SIGTERM as i32), "wait() two"); Ok(()) diff --git a/tests/tokio_windows/wait_after_die.rs b/tests/tokio_windows/wait_after_die.rs index 2a91f7d..0b1f840 100644 --- a/tests/tokio_windows/wait_after_die.rs +++ b/tests/tokio_windows/wait_after_die.rs @@ -8,7 +8,7 @@ async fn nowrap() -> Result<()> { .spawn()?; sleep(DIE_TIME).await; - let status = Box::into_pin(child.wait()).await?; + let status = child.wait().await?; assert!(status.success()); Ok(()) @@ -23,7 +23,7 @@ async fn job_object() -> Result<()> { .spawn()?; sleep(DIE_TIME).await; - let status = Box::into_pin(child.wait()).await?; + let status = child.wait().await?; assert!(status.success()); Ok(()) diff --git a/tests/tokio_windows/wait_twice.rs b/tests/tokio_windows/wait_twice.rs index a89c7f4..c79e9e6 100644 --- a/tests/tokio_windows/wait_twice.rs +++ b/tests/tokio_windows/wait_twice.rs @@ -7,10 +7,10 @@ async fn nowrap() -> Result<()> { }) .spawn()?; - let status = Box::into_pin(child.wait()).await?; + let status = child.wait().await?; assert!(status.success()); - let status = Box::into_pin(child.wait()).await?; + let status = child.wait().await?; assert!(status.success()); Ok(()) @@ -24,10 +24,10 @@ async fn job_object() -> Result<()> { .wrap(JobObject) .spawn()?; - let status = Box::into_pin(child.wait()).await?; + let status = child.wait().await?; assert!(status.success()); - let status = Box::into_pin(child.wait()).await?; + let status = child.wait().await?; assert!(status.success()); Ok(())