diff --git a/tokio/src/process/unix/mod.rs b/tokio/src/process/unix/mod.rs index c9d1035f53d..b1391bb51c9 100644 --- a/tokio/src/process/unix/mod.rs +++ b/tokio/src/process/unix/mod.rs @@ -106,7 +106,7 @@ impl OrphanQueue<StdChild> for GlobalOrphanQueue { pub(crate) enum Child { SignalReaper(Reaper<StdChild, GlobalOrphanQueue, Signal>), #[cfg(all(target_os = "linux", feature = "rt"))] - PidfdReaper(pidfd_reaper::PidfdReaper<StdChild, GlobalOrphanQueue>), + PidfdReaper(pidfd_reaper::PidfdReaper<ChildDropGuard, GlobalOrphanQueue>), } impl fmt::Debug for Child { @@ -115,21 +115,97 @@ impl fmt::Debug for Child { } } +pub(crate) struct ChildDropGuard { + child: Option<StdChild>, + kill_on_drop: bool, +} + +impl Drop for ChildDropGuard { + fn drop(&mut self) { + if let Some(child) = &mut self.child { + if self.kill_on_drop { + drop(child.kill()); + } + } + } +} + +impl ChildDropGuard { + pub(crate) fn new(child: StdChild) -> ChildDropGuard { + ChildDropGuard { + child: Some(child), + kill_on_drop: true, + } + } + + pub(crate) fn extract(mut self) -> StdChild { + self.child.take().unwrap() + } + + pub(crate) fn take_stdin(&mut self) -> Option<std::process::ChildStdin> { + self.child + .as_mut() + .expect("child has gone away") + .stdin + .take() + } + pub(crate) fn take_stdout(&mut self) -> Option<std::process::ChildStdout> { + self.child + .as_mut() + .expect("child has gone away") + .stdout + .take() + } + pub(crate) fn take_stderr(&mut self) -> Option<std::process::ChildStderr> { + self.child + .as_mut() + .expect("child has gone away") + .stderr + .take() + } + + #[cfg(all(target_os = "linux", feature = "rt"))] + pub(crate) fn dont_kill_on_drop(&mut self) { + self.kill_on_drop = false; + } + + #[cfg(all(target_os = "linux", feature = "rt"))] + pub(crate) fn inner_mut(&mut self) -> &mut StdChild { + self.child.as_mut().expect("child has gone away") + } +} + +impl Wait for ChildDropGuard { + fn id(&self) -> u32 { + self.child.as_ref().expect("child has gone away").id() + } + fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> { + self.child.as_mut().expect("child has gone away").try_wait() + } +} + +impl OrphanQueue<ChildDropGuard> for GlobalOrphanQueue { + fn push_orphan(&self, orphan: ChildDropGuard) { + get_orphan_queue().push_orphan(orphan.extract()); + } +} + pub(crate) fn spawn_child(cmd: &mut std::process::Command) -> io::Result<SpawnedChild> { - let mut child = cmd.spawn()?; - let stdin = child.stdin.take().map(stdio).transpose()?; - let stdout = child.stdout.take().map(stdio).transpose()?; - let stderr = child.stderr.take().map(stdio).transpose()?; + let mut child = ChildDropGuard::new(cmd.spawn()?); + let stdin = child.take_stdin().map(stdio).transpose()?; + let stdout = child.take_stdout().map(stdio).transpose()?; + let stderr = child.take_stderr().map(stdio).transpose()?; #[cfg(all(target_os = "linux", feature = "rt"))] match pidfd_reaper::PidfdReaper::new(child, GlobalOrphanQueue) { - Ok(pidfd_reaper) => { + Ok(mut pidfd_reaper) => { + pidfd_reaper.inner_mut().dont_kill_on_drop(); return Ok(SpawnedChild { child: Child::PidfdReaper(pidfd_reaper), stdin, stdout, stderr, - }) + }); } Err((Some(err), _child)) => return Err(err), Err((None, child_returned)) => child = child_returned, @@ -138,7 +214,7 @@ pub(crate) fn spawn_child(cmd: &mut std::process::Command) -> io::Result<Spawned let signal = signal(SignalKind::child())?; Ok(SpawnedChild { - child: Child::SignalReaper(Reaper::new(child, GlobalOrphanQueue, signal)), + child: Child::SignalReaper(Reaper::new(child.extract(), GlobalOrphanQueue, signal)), stdin, stdout, stderr, @@ -158,7 +234,7 @@ impl Child { match self { Self::SignalReaper(signal_reaper) => signal_reaper.inner_mut(), #[cfg(all(target_os = "linux", feature = "rt"))] - Self::PidfdReaper(pidfd_reaper) => pidfd_reaper.inner_mut(), + Self::PidfdReaper(pidfd_reaper) => pidfd_reaper.inner_mut().inner_mut(), } } diff --git a/tokio/src/process/windows.rs b/tokio/src/process/windows.rs index 792a9c9610b..6774ee34792 100644 --- a/tokio/src/process/windows.rs +++ b/tokio/src/process/windows.rs @@ -66,15 +66,59 @@ struct Waiting { unsafe impl Sync for Waiting {} unsafe impl Send for Waiting {} +struct DroppableChild { + child: Option<StdChild>, +} + +impl Drop for DroppableChild { + fn drop(&mut self) { + if let Some(child) = &mut self.child { + drop(child.kill()); + } + } +} + +impl DroppableChild { + pub(crate) fn new(child: StdChild) -> DroppableChild { + return DroppableChild { child: Some(child) }; + } + + pub(crate) fn take_stdin(&mut self) -> Option<std::process::ChildStdin> { + self.child + .as_mut() + .expect("child has gone away") + .stdin + .take() + } + pub(crate) fn take_stdout(&mut self) -> Option<std::process::ChildStdout> { + self.child + .as_mut() + .expect("child has gone away") + .stdout + .take() + } + pub(crate) fn take_stderr(&mut self) -> Option<std::process::ChildStderr> { + self.child + .as_mut() + .expect("child has gone away") + .stderr + .take() + } + + pub(crate) fn take(mut self) -> StdChild { + self.child.take().expect("child has gone away") + } +} + pub(crate) fn spawn_child(cmd: &mut StdCommand) -> io::Result<SpawnedChild> { - let mut child = cmd.spawn()?; - let stdin = child.stdin.take().map(stdio).transpose()?; - let stdout = child.stdout.take().map(stdio).transpose()?; - let stderr = child.stderr.take().map(stdio).transpose()?; + let mut child = DroppableChild::new(cmd.spawn()?); + let stdin = child.take_stdin().map(stdio).transpose()?; + let stdout = child.take_stdout().map(stdio).transpose()?; + let stderr = child.take_stderr().map(stdio).transpose()?; Ok(SpawnedChild { child: Child { - child, + child: child.take(), waiting: None, }, stdin,