Skip to content

Commit 68dc83d

Browse files
committed
Feat: Implement Command::spawn_with
Signed-off-by: Paul Mabileau <[email protected]>
1 parent ec4b1d7 commit 68dc83d

File tree

3 files changed

+72
-7
lines changed

3 files changed

+72
-7
lines changed

tokio/src/process/mod.rs

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ use std::future::Future;
249249
use std::io;
250250
use std::path::Path;
251251
use std::pin::Pin;
252-
use std::process::{Command as StdCommand, ExitStatus, Output, Stdio};
252+
use std::process::{Child as StdChild, Command as StdCommand, ExitStatus, Output, Stdio};
253253
use std::task::{ready, Context, Poll};
254254

255255
#[cfg(unix)]
@@ -860,8 +860,67 @@ impl Command {
860860
/// On Unix platforms this method will fail with `std::io::ErrorKind::WouldBlock`
861861
/// if the system process limit is reached (which includes other applications
862862
/// running on the system).
863+
#[inline]
863864
pub fn spawn(&mut self) -> io::Result<Child> {
864-
imp::spawn_child(&mut self.std).map(|spawned_child| Child {
865+
self.spawn_with(StdCommand::spawn)
866+
}
867+
868+
/// Executes the command as a child process with a custom spawning function,
869+
/// returning a handle to it.
870+
///
871+
/// This is identical to [`Self::spawn`] in every aspect except the spawn:
872+
/// here it customizable through the `with` parameter instead of defaulting
873+
/// to the usual spawn. In fact, [`Self::spawn`] is just [`Self::spawn_with`]
874+
/// with [`StdCommand::spawn`].
875+
///
876+
/// This is useful mostly under Windows for now, since the platform exposes
877+
/// special APIs to configure child processes when spawning them with various
878+
/// attributes that customize the exact behavior of the spawn operation.
879+
///
880+
/// # Examples
881+
///
882+
/// Basic usage:
883+
///
884+
/// ```no_run
885+
/// # async fn test() { // allow using await
886+
/// let output = tokio::process::Command::new("ls")
887+
/// .spawn_with(std::process::Command::spawn)
888+
/// .output()
889+
/// .await
890+
/// .unwrap();
891+
/// # }
892+
/// ```
893+
///
894+
/// Actually customizing the spawn under Windows:
895+
///
896+
/// ```ignore
897+
/// # #[cfg(windows)] // Windows-only nightly APIs are used here.
898+
/// # async fn test() { // Allow using await.
899+
/// use std::os::windows::io::AsRawHandle;
900+
/// use std::os::windows::process::{CommandExt, ProcThreadAttributeList};
901+
/// use tokio::process::Command;
902+
///
903+
/// let parent = Command::new("cmd").spawn().unwrap();
904+
/// let parent_process_handle = parent.as_raw_handle();
905+
///
906+
/// const PROC_THREAD_ATTRIBUTE_PARENT_PROCESS: usize = 0x00020000;
907+
/// let mut attribute_list = ProcThreadAttributeList::build()
908+
/// .attribute(PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &parent_process_handle)
909+
/// .finish()
910+
/// .unwrap();
911+
///
912+
/// let output = Command::new("ls")
913+
/// .spawn_with(|cmd| cmd.spawn_with_attributes(&attribute_list))
914+
/// .output()
915+
/// .await
916+
/// .unwrap();
917+
/// # }
918+
/// ```
919+
pub fn spawn_with(
920+
&mut self,
921+
with: impl Fn(&mut StdCommand) -> io::Result<StdChild>,
922+
) -> io::Result<Child> {
923+
imp::spawn_child_with(&mut self.std, with).map(|spawned_child| Child {
865924
child: FusedChild::Child(ChildDropGuard {
866925
inner: spawned_child.child,
867926
kill_on_drop: self.kill_on_drop,

tokio/src/process/unix/mod.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ use std::future::Future;
4444
use std::io;
4545
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
4646
use std::pin::Pin;
47-
use std::process::{Child as StdChild, ExitStatus, Stdio};
47+
use std::process::{Child as StdChild, Command as StdCommand, ExitStatus, Stdio};
4848
use std::task::Context;
4949
use std::task::Poll;
5050

@@ -115,8 +115,11 @@ impl fmt::Debug for Child {
115115
}
116116
}
117117

118-
pub(crate) fn spawn_child(cmd: &mut std::process::Command) -> io::Result<SpawnedChild> {
119-
let mut child = cmd.spawn()?;
118+
pub(crate) fn spawn_child_with(
119+
cmd: &mut StdCommand,
120+
with: impl Fn(&mut StdCommand) -> io::Result<StdChild>,
121+
) -> io::Result<SpawnedChild> {
122+
let mut child = with(cmd)?;
120123
let stdin = child.stdin.take().map(stdio).transpose()?;
121124
let stdout = child.stdout.take().map(stdio).transpose()?;
122125
let stderr = child.stderr.take().map(stdio).transpose()?;

tokio/src/process/windows.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,11 @@ struct Waiting {
6666
unsafe impl Sync for Waiting {}
6767
unsafe impl Send for Waiting {}
6868

69-
pub(crate) fn spawn_child(cmd: &mut StdCommand) -> io::Result<SpawnedChild> {
70-
let mut child = cmd.spawn()?;
69+
pub(crate) fn spawn_child_with(
70+
cmd: &mut StdCommand,
71+
with: impl Fn(&mut StdCommand) -> io::Result<StdChild>,
72+
) -> io::Result<SpawnedChild> {
73+
let mut child = with(cmd)?;
7174
let stdin = child.stdin.take().map(stdio).transpose()?;
7275
let stdout = child.stdout.take().map(stdio).transpose()?;
7376
let stderr = child.stderr.take().map(stdio).transpose()?;

0 commit comments

Comments
 (0)