Skip to content

Commit 2701079

Browse files
committed
Feat(process): Implement Command::spawn_with
See #7238. Signed-off-by: Paul Mabileau <[email protected]>
1 parent c3037ad commit 2701079

File tree

4 files changed

+98
-8
lines changed

4 files changed

+98
-8
lines changed

spellcheck.dic

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
300
1+
302
22
&
33
+
44
<
@@ -70,6 +70,7 @@ connectionless
7070
coroutines
7171
cpu
7272
cpus
73+
customizable
7374
Customizable
7475
datagram
7576
Datagram
@@ -169,6 +170,7 @@ mio's
169170
miri
170171
misconfigured
171172
mock's
173+
monomorphization
172174
mpmc
173175
mpsc
174176
multi

tokio/src/process/mod.rs

Lines changed: 92 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,98 @@ 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+
// On two lines to circumvent a mutable borrow check failure.
866+
let child = self.std.spawn()?;
867+
self.build_child(child)
868+
}
869+
870+
/// Executes the command as a child process with a custom spawning function,
871+
/// returning a handle to it.
872+
///
873+
/// This is identical to [`Self::spawn`] in every aspect except the spawn:
874+
/// here, it is customizable through the `with` parameter instead of
875+
/// defaulting to the usual spawn. In fact, [`Self::spawn`] is just
876+
/// [`Self::spawn_with`] with [`StdCommand::spawn`].
877+
///
878+
/// This is useful mostly under Windows for now, since the platform exposes
879+
/// special APIs to configure child processes when spawning them with various
880+
/// attributes that customize the exact behavior of the spawn operation.
881+
///
882+
/// # Examples
883+
///
884+
/// Basic usage:
885+
///
886+
/// ```no_run
887+
/// # async fn test() { // allow using await
888+
/// use std::process::Stdio;
889+
///
890+
/// let output = tokio::process::Command::new("ls")
891+
/// .stdin(Stdio::null())
892+
/// .stdout(Stdio::piped())
893+
/// .stderr(Stdio::piped())
894+
/// .spawn_with(std::process::Command::spawn)
895+
/// .unwrap()
896+
/// .wait_with_output()
897+
/// .await
898+
/// .unwrap();
899+
/// # }
900+
/// ```
901+
///
902+
/// Actually customizing the spawn under Windows:
903+
///
904+
/// ```ignore
905+
/// # #![feature(windows_process_extensions_raw_attribute)]
906+
/// # #[cfg(windows)] // Windows-only nightly APIs are used here.
907+
/// # async fn test() { // Allow using await.
908+
/// use std::os::windows::process::{CommandExt, ProcThreadAttributeList};
909+
/// use std::process::Stdio;
910+
/// use tokio::process::Command;
911+
///
912+
/// let parent = Command::new("cmd").spawn().unwrap();
913+
/// let parent_process_handle = parent.raw_handle();
914+
///
915+
/// const PROC_THREAD_ATTRIBUTE_PARENT_PROCESS: usize = 0x00020000;
916+
/// let attribute_list = ProcThreadAttributeList::build()
917+
/// .attribute(PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &parent_process_handle)
918+
/// .finish()
919+
/// .unwrap();
920+
///
921+
/// let _output = Command::new("ls")
922+
/// .stdin(Stdio::null())
923+
/// .stdout(Stdio::piped())
924+
/// .stderr(Stdio::piped())
925+
/// .spawn_with(|cmd| cmd.spawn_with_attributes(&attribute_list))
926+
/// .unwrap()
927+
/// .wait_with_output()
928+
/// .await
929+
/// .unwrap();
930+
/// # }
931+
/// ```
932+
#[cfg(tokio_unstable)]
933+
#[cfg_attr(docsrs, doc(cfg(tokio_unstable)))]
934+
#[inline]
935+
pub fn spawn_with(
936+
&mut self,
937+
with: impl Fn(&mut StdCommand) -> io::Result<StdChild>,
938+
) -> io::Result<Child> {
939+
// On two lines to circumvent a mutable borrow check failure.
940+
let child = with(&mut self.std)?;
941+
self.build_child(child)
942+
}
943+
944+
/// Small indirection for the spawn implementations.
945+
///
946+
/// This is introduced for [`Self::spawn`] and [`Self::spawn_with`] to use:
947+
/// [`Self::spawn`] cannot depend directly on on [`Self::spawn_with`] since
948+
/// it is behind `tokio_unstable`. It also serves as a way to reduce
949+
/// monomorphization bloat by taking in an already-spawned child process
950+
/// instead of a command and custom spawn function.
951+
fn build_child(&self, child: StdChild) -> io::Result<Child> {
952+
let spawned_child = imp::build_child(child)?;
953+
954+
Ok(Child {
865955
child: FusedChild::Child(ChildDropGuard {
866956
inner: spawned_child.child,
867957
kill_on_drop: self.kill_on_drop,

tokio/src/process/unix/mod.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,7 @@ 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 build_child(mut child: StdChild) -> io::Result<SpawnedChild> {
120119
let stdin = child.stdin.take().map(stdio).transpose()?;
121120
let stdout = child.stdout.take().map(stdio).transpose()?;
122121
let stderr = child.stderr.take().map(stdio).transpose()?;

tokio/src/process/windows.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ use std::io;
2727
use std::os::windows::prelude::{AsRawHandle, IntoRawHandle, OwnedHandle, RawHandle};
2828
use std::pin::Pin;
2929
use std::process::Stdio;
30-
use std::process::{Child as StdChild, Command as StdCommand, ExitStatus};
30+
use std::process::{Child as StdChild, ExitStatus};
3131
use std::sync::Arc;
3232
use std::task::{Context, Poll};
3333

@@ -66,8 +66,7 @@ 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 build_child(mut child: StdChild) -> io::Result<SpawnedChild> {
7170
let stdin = child.stdin.take().map(stdio).transpose()?;
7271
let stdout = child.stdout.take().map(stdio).transpose()?;
7372
let stderr = child.stderr.take().map(stdio).transpose()?;

0 commit comments

Comments
 (0)