Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion spellcheck.dic
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
300
302
&
+
<
Expand Down Expand Up @@ -70,6 +70,7 @@ connectionless
coroutines
cpu
cpus
customizable
Customizable
datagram
Datagram
Expand Down Expand Up @@ -169,6 +170,7 @@ mio's
miri
misconfigured
mock's
monomorphization
mpmc
mpsc
multi
Expand Down
94 changes: 92 additions & 2 deletions tokio/src/process/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ use std::future::Future;
use std::io;
use std::path::Path;
use std::pin::Pin;
use std::process::{Command as StdCommand, ExitStatus, Output, Stdio};
use std::process::{Child as StdChild, Command as StdCommand, ExitStatus, Output, Stdio};
use std::task::{ready, Context, Poll};

#[cfg(unix)]
Expand Down Expand Up @@ -860,8 +860,98 @@ impl Command {
/// On Unix platforms this method will fail with `std::io::ErrorKind::WouldBlock`
/// if the system process limit is reached (which includes other applications
/// running on the system).
#[inline]
pub fn spawn(&mut self) -> io::Result<Child> {
imp::spawn_child(&mut self.std).map(|spawned_child| Child {
// On two lines to circumvent a mutable borrow check failure.
let child = self.std.spawn()?;
self.build_child(child)
}

/// Executes the command as a child process with a custom spawning function,
/// returning a handle to it.
///
/// This is identical to [`Self::spawn`] in every aspect except the spawn:
/// here, it is customizable through the `with` parameter instead of
/// defaulting to the usual spawn. In fact, [`Self::spawn`] is just
/// [`Self::spawn_with`] with [`StdCommand::spawn`].
///
/// This is useful mostly under Windows for now, since the platform exposes
/// special APIs to configure child processes when spawning them with various
/// attributes that customize the exact behavior of the spawn operation.
///
/// # Examples
///
/// Basic usage:
///
/// ```no_run
/// # async fn test() { // allow using await
/// use std::process::Stdio;
///
/// let output = tokio::process::Command::new("ls")
/// .stdin(Stdio::null())
/// .stdout(Stdio::piped())
/// .stderr(Stdio::piped())
/// .spawn_with(std::process::Command::spawn)
/// .unwrap()
/// .wait_with_output()
/// .await
/// .unwrap();
/// # }
/// ```
///
/// Actually customizing the spawn under Windows:
///
/// ```ignore
/// #![feature(windows_process_extensions_raw_attribute)]
/// # #[cfg(windows)] // Windows-only nightly APIs are used here.
/// # async fn test() { // Allow using await.
/// use std::os::windows::process::{CommandExt, ProcThreadAttributeList};
/// use std::process::Stdio;
/// use tokio::process::Command;
///
/// let parent = Command::new("cmd").spawn().unwrap();
/// let parent_process_handle = parent.raw_handle();
///
/// const PROC_THREAD_ATTRIBUTE_PARENT_PROCESS: usize = 0x00020000;
/// let attribute_list = ProcThreadAttributeList::build()
/// .attribute(PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &parent_process_handle)
/// .finish()
/// .unwrap();
///
/// let _output = Command::new("ls")
/// .stdin(Stdio::null())
/// .stdout(Stdio::piped())
/// .stderr(Stdio::piped())
/// .spawn_with(|cmd| cmd.spawn_with_attributes(&attribute_list))
/// .unwrap()
/// .wait_with_output()
/// .await
/// .unwrap();
/// # }
/// ```
#[cfg(tokio_unstable)]
#[cfg_attr(docsrs, doc(cfg(tokio_unstable)))]
#[inline]
pub fn spawn_with(
&mut self,
with: impl Fn(&mut StdCommand) -> io::Result<StdChild>,
) -> io::Result<Child> {
// On two lines to circumvent a mutable borrow check failure.
let child = with(&mut self.std)?;
self.build_child(child)
}

/// Small indirection for the spawn implementations.
///
/// This is introduced for [`Self::spawn`] and [`Self::spawn_with`] to use:
/// [`Self::spawn`] cannot depend directly on on [`Self::spawn_with`] since
/// it is behind `tokio_unstable`. It also serves as a way to reduce
/// monomorphization bloat by taking in an already-spawned child process
/// instead of a command and custom spawn function.
fn build_child(&self, child: StdChild) -> io::Result<Child> {
let spawned_child = imp::build_child(child)?;

Ok(Child {
child: FusedChild::Child(ChildDropGuard {
inner: spawned_child.child,
kill_on_drop: self.kill_on_drop,
Expand Down
3 changes: 1 addition & 2 deletions tokio/src/process/unix/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,7 @@ impl fmt::Debug for Child {
}
}

pub(crate) fn spawn_child(cmd: &mut std::process::Command) -> io::Result<SpawnedChild> {
let mut child = cmd.spawn()?;
pub(crate) fn build_child(mut child: StdChild) -> io::Result<SpawnedChild> {
let stdin = child.stdin.take().map(stdio).transpose()?;
let stdout = child.stdout.take().map(stdio).transpose()?;
let stderr = child.stderr.take().map(stdio).transpose()?;
Expand Down
5 changes: 2 additions & 3 deletions tokio/src/process/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ use std::io;
use std::os::windows::prelude::{AsRawHandle, IntoRawHandle, OwnedHandle, RawHandle};
use std::pin::Pin;
use std::process::Stdio;
use std::process::{Child as StdChild, Command as StdCommand, ExitStatus};
use std::process::{Child as StdChild, ExitStatus};
use std::sync::Arc;
use std::task::{Context, Poll};

Expand Down Expand Up @@ -66,8 +66,7 @@ struct Waiting {
unsafe impl Sync for Waiting {}
unsafe impl Send for Waiting {}

pub(crate) fn spawn_child(cmd: &mut StdCommand) -> io::Result<SpawnedChild> {
let mut child = cmd.spawn()?;
pub(crate) fn build_child(mut child: StdChild) -> io::Result<SpawnedChild> {
let stdin = child.stdin.take().map(stdio).transpose()?;
let stdout = child.stdout.take().map(stdio).transpose()?;
let stderr = child.stderr.take().map(stdio).transpose()?;
Expand Down
Loading