Skip to content

Commit b04752a

Browse files
committed
Feat: Implement Command::spawn_with
Signed-off-by: Paul Mabileau <[email protected]>
1 parent 817fa60 commit b04752a

File tree

4 files changed

+84
-7
lines changed

4 files changed

+84
-7
lines changed

spellcheck.dic

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ coroutines
7171
cpu
7272
cpus
7373
Customizable
74+
customizable
7475
datagram
7576
Datagram
7677
datagrams

tokio/src/process/mod.rs

Lines changed: 72 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,78 @@ 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 is customizable through the `with` parameter instead of
873+
/// defaulting to the usual spawn. In fact, [`Self::spawn`] is just
874+
/// [`Self::spawn_with`] 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+
/// use std::process::Stdio;
887+
///
888+
/// let output = tokio::process::Command::new("ls")
889+
/// .stdin(Stdio::null())
890+
/// .stdout(Stdio::piped())
891+
/// .stderr(Stdio::piped())
892+
/// .spawn_with(std::process::Command::spawn)
893+
/// .unwrap()
894+
/// .wait_with_output()
895+
/// .await
896+
/// .unwrap();
897+
/// # }
898+
/// ```
899+
///
900+
/// Actually customizing the spawn under Windows:
901+
///
902+
/// ```ignore
903+
/// # #[cfg(windows)] // Windows-only nightly APIs are used here.
904+
/// # async fn test() { // Allow using await.
905+
/// use std::os::windows::io::AsRawHandle;
906+
/// use std::os::windows::process::{CommandExt, ProcThreadAttributeList};
907+
/// use std::process::Stdio;
908+
/// use tokio::process::Command;
909+
///
910+
/// let parent = Command::new("cmd").spawn().unwrap();
911+
/// let parent_process_handle = parent.as_raw_handle();
912+
///
913+
/// const PROC_THREAD_ATTRIBUTE_PARENT_PROCESS: usize = 0x00020000;
914+
/// let mut attribute_list = ProcThreadAttributeList::build()
915+
/// .attribute(PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &parent_process_handle)
916+
/// .finish()
917+
/// .unwrap();
918+
///
919+
/// let output = Command::new("ls")
920+
/// .stdin(Stdio::null())
921+
/// .stdout(Stdio::piped())
922+
/// .stderr(Stdio::piped())
923+
/// .spawn_with(|cmd| cmd.spawn_with_attributes(&attribute_list))
924+
/// .unwrap()
925+
/// .wait_with_output()
926+
/// .await
927+
/// .unwrap();
928+
/// # }
929+
/// ```
930+
pub fn spawn_with(
931+
&mut self,
932+
with: impl Fn(&mut StdCommand) -> io::Result<StdChild>,
933+
) -> io::Result<Child> {
934+
imp::spawn_child_with(&mut self.std, with).map(|spawned_child| Child {
865935
child: FusedChild::Child(ChildDropGuard {
866936
inner: spawned_child.child,
867937
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)