std: Don't always create stdin for children

For example if `Command::output` or `Command::status` is used then stdin is just
immediately closed. Add an option for this so as an optimization we can avoid
creating pipes entirely.

This should help reduce the number of active file descriptors when spawning
processes on Unix and the number of active handles on Windows.
This commit is contained in:
Alex Crichton 2016-02-12 10:28:03 -08:00
parent d46c99abe8
commit 6afa32a250
3 changed files with 16 additions and 10 deletions

View File

@ -295,7 +295,7 @@ impl Command {
/// By default, stdin, stdout and stderr are inherited from the parent.
#[stable(feature = "process", since = "1.0.0")]
pub fn spawn(&mut self) -> io::Result<Child> {
self.inner.spawn(imp::Stdio::Inherit).map(Child::from_inner)
self.inner.spawn(imp::Stdio::Inherit, true).map(Child::from_inner)
}
/// Executes the command as a child process, waiting for it to finish and
@ -318,7 +318,7 @@ impl Command {
/// ```
#[stable(feature = "process", since = "1.0.0")]
pub fn output(&mut self) -> io::Result<Output> {
self.inner.spawn(imp::Stdio::MakePipe).map(Child::from_inner)
self.inner.spawn(imp::Stdio::MakePipe, false).map(Child::from_inner)
.and_then(|p| p.wait_with_output())
}
@ -340,7 +340,8 @@ impl Command {
/// ```
#[stable(feature = "process", since = "1.0.0")]
pub fn status(&mut self) -> io::Result<ExitStatus> {
self.spawn().and_then(|mut p| p.wait())
self.inner.spawn(imp::Stdio::Inherit, false).map(Child::from_inner)
.and_then(|mut p| p.wait())
}
}

View File

@ -216,7 +216,7 @@ impl Command {
self.stderr = Some(stderr);
}
pub fn spawn(&mut self, default: Stdio)
pub fn spawn(&mut self, default: Stdio, needs_stdin: bool)
-> io::Result<(Process, StdioPipes)> {
const CLOEXEC_MSG_FOOTER: &'static [u8] = b"NOEX";
@ -225,7 +225,7 @@ impl Command {
"nul byte found in provided data"));
}
let (ours, theirs) = try!(self.setup_io(default));
let (ours, theirs) = try!(self.setup_io(default, needs_stdin));
let (input, output) = try!(sys::pipe::anon_pipe());
let pid = unsafe {
@ -298,7 +298,7 @@ impl Command {
"nul byte found in provided data")
}
match self.setup_io(default) {
match self.setup_io(default, true) {
Ok((_, theirs)) => unsafe { self.do_exec(theirs) },
Err(e) => e,
}
@ -408,8 +408,11 @@ impl Command {
}
fn setup_io(&self, default: Stdio) -> io::Result<(StdioPipes, ChildPipes)> {
let stdin = self.stdin.as_ref().unwrap_or(&default);
fn setup_io(&self, default: Stdio, needs_stdin: bool)
-> io::Result<(StdioPipes, ChildPipes)> {
let null = Stdio::Null;
let default_stdin = if needs_stdin {&default} else {&null};
let stdin = self.stdin.as_ref().unwrap_or(default_stdin);
let stdout = self.stdout.as_ref().unwrap_or(&default);
let stderr = self.stderr.as_ref().unwrap_or(&default);
let (their_stdin, our_stdin) = try!(stdin.to_child_stdio(true));

View File

@ -123,7 +123,7 @@ impl Command {
self.stderr = Some(stderr);
}
pub fn spawn(&mut self, default: Stdio)
pub fn spawn(&mut self, default: Stdio, needs_stdin: bool)
-> io::Result<(Process, StdioPipes)> {
// To have the spawning semantics of unix/windows stay the same, we need
// to read the *child's* PATH if one is provided. See #15149 for more
@ -181,7 +181,9 @@ impl Command {
stdout: None,
stderr: None,
};
let stdin = self.stdin.as_ref().unwrap_or(&default);
let null = Stdio::Null;
let default_stdin = if needs_stdin {&default} else {&null};
let stdin = self.stdin.as_ref().unwrap_or(default_stdin);
let stdout = self.stdout.as_ref().unwrap_or(&default);
let stderr = self.stderr.as_ref().unwrap_or(&default);
let stdin = try!(stdin.to_handle(c::STD_INPUT_HANDLE, &mut pipes.stdin));