Skip to content

Commit 3f4227a

Browse files
committed
Auto merge of #31409 - alexcrichton:command-exec, r=aturon
These commits are an implementation of rust-lang/rfcs#1359 which is tracked via #31398. The `before_exec` implementation fit easily with the current process spawning framework we have, but unfortunately the `exec` implementation required a bit of a larger refactoring. The stdio handles were all largely managed as implementation details of `std::process` and the `exec` function lived in `std::sys`, so the two didn't have access to one another. I took this as a sign that a deeper refactoring was necessary, and I personally feel that the end result is cleaner for both Windows and Unix. The commits should be separated nicely for reviewing (or all at once if you're feeling ambitious), but the changes made here were: * The process spawning on Unix was refactored in to a pre-exec and post-exec function. The post-exec function isn't allowed to do any allocations of any form, and management of transmitting errors back to the parent is managed by the pre-exec function (as it's the one that actually forks). * Some management of the exit status was pushed into platform-specific modules. On Unix we must cache the return value of `wait` as the pid is consumed after we wait on it, but on Windows we can just keep querying the system because the handle stays valid. * The `Stdio::None` variant was renamed to `Stdio::Null` to better reflect what it's doing. * The global lock on `CreateProcess` is now correctly positioned to avoid unintended inheritance of pipe handles that other threads are sending to their child processes. After a more careful reading of the article referenced the race is not in `CreateProcess` itself, but rather the property that handles are unintentionally shared. * All stdio management now happens in platform-specific modules. This provides a cleaner implementation/interpretation for `FromFraw{Fd,Handle}` for each platform as well as a cleaner transition from a configuration to what-to-do once we actually need to do the spawn. With these refactorings in place, implementing `before_exec` and `exec` ended up both being pretty trivial! (each in their own commit)
2 parents 5d771cd + d9c6a51 commit 3f4227a

File tree

9 files changed

+826
-562
lines changed

9 files changed

+826
-562
lines changed

src/libstd/process.rs

+52-130
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,16 @@
1111
//! Working with processes.
1212
1313
#![stable(feature = "process", since = "1.0.0")]
14-
#![allow(non_upper_case_globals)]
1514

1615
use prelude::v1::*;
1716
use io::prelude::*;
1817

1918
use ffi::OsStr;
2019
use fmt;
21-
use io::{self, Error, ErrorKind};
22-
use path;
20+
use io;
21+
use path::Path;
2322
use str;
24-
use sys::pipe::{self, AnonPipe};
23+
use sys::pipe::AnonPipe;
2524
use sys::process as imp;
2625
use sys_common::{AsInner, AsInnerMut, FromInner, IntoInner};
2726
use thread::{self, JoinHandle};
@@ -61,9 +60,6 @@ use thread::{self, JoinHandle};
6160
pub struct Child {
6261
handle: imp::Process,
6362

64-
/// None until wait() or wait_with_output() is called.
65-
status: Option<imp::ExitStatus>,
66-
6763
/// The handle for writing to the child's stdin, if it has been captured
6864
#[stable(feature = "process", since = "1.0.0")]
6965
pub stdin: Option<ChildStdin>,
@@ -81,6 +77,17 @@ impl AsInner<imp::Process> for Child {
8177
fn as_inner(&self) -> &imp::Process { &self.handle }
8278
}
8379

80+
impl FromInner<(imp::Process, imp::StdioPipes)> for Child {
81+
fn from_inner((handle, io): (imp::Process, imp::StdioPipes)) -> Child {
82+
Child {
83+
handle: handle,
84+
stdin: io.stdin.map(ChildStdin::from_inner),
85+
stdout: io.stdout.map(ChildStdout::from_inner),
86+
stderr: io.stderr.map(ChildStderr::from_inner),
87+
}
88+
}
89+
}
90+
8491
impl IntoInner<imp::Process> for Child {
8592
fn into_inner(self) -> imp::Process { self.handle }
8693
}
@@ -110,6 +117,12 @@ impl IntoInner<AnonPipe> for ChildStdin {
110117
fn into_inner(self) -> AnonPipe { self.inner }
111118
}
112119

120+
impl FromInner<AnonPipe> for ChildStdin {
121+
fn from_inner(pipe: AnonPipe) -> ChildStdin {
122+
ChildStdin { inner: pipe }
123+
}
124+
}
125+
113126
/// A handle to a child process's stdout
114127
#[stable(feature = "process", since = "1.0.0")]
115128
pub struct ChildStdout {
@@ -131,6 +144,12 @@ impl IntoInner<AnonPipe> for ChildStdout {
131144
fn into_inner(self) -> AnonPipe { self.inner }
132145
}
133146

147+
impl FromInner<AnonPipe> for ChildStdout {
148+
fn from_inner(pipe: AnonPipe) -> ChildStdout {
149+
ChildStdout { inner: pipe }
150+
}
151+
}
152+
134153
/// A handle to a child process's stderr
135154
#[stable(feature = "process", since = "1.0.0")]
136155
pub struct ChildStderr {
@@ -152,6 +171,12 @@ impl IntoInner<AnonPipe> for ChildStderr {
152171
fn into_inner(self) -> AnonPipe { self.inner }
153172
}
154173

174+
impl FromInner<AnonPipe> for ChildStderr {
175+
fn from_inner(pipe: AnonPipe) -> ChildStderr {
176+
ChildStderr { inner: pipe }
177+
}
178+
}
179+
155180
/// The `Command` type acts as a process builder, providing fine-grained control
156181
/// over how a new process should be spawned. A default configuration can be
157182
/// generated using `Command::new(program)`, where `program` gives a path to the
@@ -171,11 +196,6 @@ impl IntoInner<AnonPipe> for ChildStderr {
171196
#[stable(feature = "process", since = "1.0.0")]
172197
pub struct Command {
173198
inner: imp::Command,
174-
175-
// Details explained in the builder methods
176-
stdin: Option<Stdio>,
177-
stdout: Option<Stdio>,
178-
stderr: Option<Stdio>,
179199
}
180200

181201
impl Command {
@@ -191,12 +211,7 @@ impl Command {
191211
/// otherwise configure the process.
192212
#[stable(feature = "process", since = "1.0.0")]
193213
pub fn new<S: AsRef<OsStr>>(program: S) -> Command {
194-
Command {
195-
inner: imp::Command::new(program.as_ref()),
196-
stdin: None,
197-
stdout: None,
198-
stderr: None,
199-
}
214+
Command { inner: imp::Command::new(program.as_ref()) }
200215
}
201216

202217
/// Add an argument to pass to the program.
@@ -209,7 +224,9 @@ impl Command {
209224
/// Add multiple arguments to pass to the program.
210225
#[stable(feature = "process", since = "1.0.0")]
211226
pub fn args<S: AsRef<OsStr>>(&mut self, args: &[S]) -> &mut Command {
212-
self.inner.args(args.iter().map(AsRef::as_ref));
227+
for arg in args {
228+
self.arg(arg.as_ref());
229+
}
213230
self
214231
}
215232

@@ -241,65 +258,38 @@ impl Command {
241258

242259
/// Sets the working directory for the child process.
243260
#[stable(feature = "process", since = "1.0.0")]
244-
pub fn current_dir<P: AsRef<path::Path>>(&mut self, dir: P) -> &mut Command {
261+
pub fn current_dir<P: AsRef<Path>>(&mut self, dir: P) -> &mut Command {
245262
self.inner.cwd(dir.as_ref().as_ref());
246263
self
247264
}
248265

249266
/// Configuration for the child process's stdin handle (file descriptor 0).
250267
#[stable(feature = "process", since = "1.0.0")]
251268
pub fn stdin(&mut self, cfg: Stdio) -> &mut Command {
252-
self.stdin = Some(cfg);
269+
self.inner.stdin(cfg.0);
253270
self
254271
}
255272

256273
/// Configuration for the child process's stdout handle (file descriptor 1).
257274
#[stable(feature = "process", since = "1.0.0")]
258275
pub fn stdout(&mut self, cfg: Stdio) -> &mut Command {
259-
self.stdout = Some(cfg);
276+
self.inner.stdout(cfg.0);
260277
self
261278
}
262279

263280
/// Configuration for the child process's stderr handle (file descriptor 2).
264281
#[stable(feature = "process", since = "1.0.0")]
265282
pub fn stderr(&mut self, cfg: Stdio) -> &mut Command {
266-
self.stderr = Some(cfg);
283+
self.inner.stderr(cfg.0);
267284
self
268285
}
269286

270-
fn spawn_inner(&self, default_io: StdioImp) -> io::Result<Child> {
271-
let default_io = Stdio(default_io);
272-
273-
// See comment on `setup_io` for what `_drop_later` is.
274-
let (their_stdin, our_stdin, _drop_later) = try!(
275-
setup_io(self.stdin.as_ref().unwrap_or(&default_io), true)
276-
);
277-
let (their_stdout, our_stdout, _drop_later) = try!(
278-
setup_io(self.stdout.as_ref().unwrap_or(&default_io), false)
279-
);
280-
let (their_stderr, our_stderr, _drop_later) = try!(
281-
setup_io(self.stderr.as_ref().unwrap_or(&default_io), false)
282-
);
283-
284-
match imp::Process::spawn(&self.inner, their_stdin, their_stdout,
285-
their_stderr) {
286-
Err(e) => Err(e),
287-
Ok(handle) => Ok(Child {
288-
handle: handle,
289-
status: None,
290-
stdin: our_stdin.map(|fd| ChildStdin { inner: fd }),
291-
stdout: our_stdout.map(|fd| ChildStdout { inner: fd }),
292-
stderr: our_stderr.map(|fd| ChildStderr { inner: fd }),
293-
})
294-
}
295-
}
296-
297287
/// Executes the command as a child process, returning a handle to it.
298288
///
299289
/// By default, stdin, stdout and stderr are inherited from the parent.
300290
#[stable(feature = "process", since = "1.0.0")]
301291
pub fn spawn(&mut self) -> io::Result<Child> {
302-
self.spawn_inner(StdioImp::Inherit)
292+
self.inner.spawn(imp::Stdio::Inherit).map(Child::from_inner)
303293
}
304294

305295
/// Executes the command as a child process, waiting for it to finish and
@@ -322,7 +312,8 @@ impl Command {
322312
/// ```
323313
#[stable(feature = "process", since = "1.0.0")]
324314
pub fn output(&mut self) -> io::Result<Output> {
325-
self.spawn_inner(StdioImp::MakePipe).and_then(|p| p.wait_with_output())
315+
self.inner.spawn(imp::Stdio::MakePipe).map(Child::from_inner)
316+
.and_then(|p| p.wait_with_output())
326317
}
327318

328319
/// Executes a command as a child process, waiting for it to finish and
@@ -365,33 +356,6 @@ impl AsInnerMut<imp::Command> for Command {
365356
fn as_inner_mut(&mut self) -> &mut imp::Command { &mut self.inner }
366357
}
367358

368-
// Takes a `Stdio` configuration (this module) and whether the to-be-owned
369-
// handle will be readable.
370-
//
371-
// Returns a triple of (stdio to spawn with, stdio to store, stdio to drop). The
372-
// stdio to spawn with is passed down to the `sys` module and indicates how the
373-
// stdio stream should be set up. The "stdio to store" is an object which
374-
// should be returned in the `Child` that makes its way out. The "stdio to drop"
375-
// represents the raw value of "stdio to spawn with", but is the owned variant
376-
// for it. This needs to be dropped after the child spawns
377-
fn setup_io(io: &Stdio, readable: bool)
378-
-> io::Result<(imp::Stdio, Option<AnonPipe>, Option<AnonPipe>)>
379-
{
380-
Ok(match io.0 {
381-
StdioImp::MakePipe => {
382-
let (reader, writer) = try!(pipe::anon_pipe());
383-
if readable {
384-
(imp::Stdio::Raw(reader.raw()), Some(writer), Some(reader))
385-
} else {
386-
(imp::Stdio::Raw(writer.raw()), Some(reader), Some(writer))
387-
}
388-
}
389-
StdioImp::Raw(ref owned) => (imp::Stdio::Raw(owned.raw()), None, None),
390-
StdioImp::Inherit => (imp::Stdio::Inherit, None, None),
391-
StdioImp::None => (imp::Stdio::None, None, None),
392-
})
393-
}
394-
395359
/// The output of a finished process.
396360
#[derive(PartialEq, Eq, Clone)]
397361
#[stable(feature = "process", since = "1.0.0")]
@@ -435,34 +399,26 @@ impl fmt::Debug for Output {
435399

436400
/// Describes what to do with a standard I/O stream for a child process.
437401
#[stable(feature = "process", since = "1.0.0")]
438-
pub struct Stdio(StdioImp);
439-
440-
// The internal enum for stdio setup; see below for descriptions.
441-
enum StdioImp {
442-
MakePipe,
443-
Raw(imp::RawStdio),
444-
Inherit,
445-
None,
446-
}
402+
pub struct Stdio(imp::Stdio);
447403

448404
impl Stdio {
449405
/// A new pipe should be arranged to connect the parent and child processes.
450406
#[stable(feature = "process", since = "1.0.0")]
451-
pub fn piped() -> Stdio { Stdio(StdioImp::MakePipe) }
407+
pub fn piped() -> Stdio { Stdio(imp::Stdio::MakePipe) }
452408

453409
/// The child inherits from the corresponding parent descriptor.
454410
#[stable(feature = "process", since = "1.0.0")]
455-
pub fn inherit() -> Stdio { Stdio(StdioImp::Inherit) }
411+
pub fn inherit() -> Stdio { Stdio(imp::Stdio::Inherit) }
456412

457413
/// This stream will be ignored. This is the equivalent of attaching the
458414
/// stream to `/dev/null`
459415
#[stable(feature = "process", since = "1.0.0")]
460-
pub fn null() -> Stdio { Stdio(StdioImp::None) }
416+
pub fn null() -> Stdio { Stdio(imp::Stdio::Null) }
461417
}
462418

463-
impl FromInner<imp::RawStdio> for Stdio {
464-
fn from_inner(inner: imp::RawStdio) -> Stdio {
465-
Stdio(StdioImp::Raw(inner))
419+
impl FromInner<imp::Stdio> for Stdio {
420+
fn from_inner(inner: imp::Stdio) -> Stdio {
421+
Stdio(inner)
466422
}
467423
}
468424

@@ -506,34 +462,7 @@ impl Child {
506462
/// SIGKILL on unix platforms.
507463
#[stable(feature = "process", since = "1.0.0")]
508464
pub fn kill(&mut self) -> io::Result<()> {
509-
#[cfg(unix)] fn collect_status(p: &mut Child) {
510-
// On Linux (and possibly other unices), a process that has exited will
511-
// continue to accept signals because it is "defunct". The delivery of
512-
// signals will only fail once the child has been reaped. For this
513-
// reason, if the process hasn't exited yet, then we attempt to collect
514-
// their status with WNOHANG.
515-
if p.status.is_none() {
516-
match p.handle.try_wait() {
517-
Some(status) => { p.status = Some(status); }
518-
None => {}
519-
}
520-
}
521-
}
522-
#[cfg(windows)] fn collect_status(_p: &mut Child) {}
523-
524-
collect_status(self);
525-
526-
// if the process has finished, and therefore had waitpid called,
527-
// and we kill it, then on unix we might ending up killing a
528-
// newer process that happens to have the same (re-used) id
529-
if self.status.is_some() {
530-
return Err(Error::new(
531-
ErrorKind::InvalidInput,
532-
"invalid argument: can't kill an exited process",
533-
))
534-
}
535-
536-
unsafe { self.handle.kill() }
465+
self.handle.kill()
537466
}
538467

539468
/// Returns the OS-assigned process identifier associated with this child.
@@ -553,14 +482,7 @@ impl Child {
553482
#[stable(feature = "process", since = "1.0.0")]
554483
pub fn wait(&mut self) -> io::Result<ExitStatus> {
555484
drop(self.stdin.take());
556-
match self.status {
557-
Some(code) => Ok(ExitStatus(code)),
558-
None => {
559-
let status = try!(self.handle.wait());
560-
self.status = Some(status);
561-
Ok(ExitStatus(status))
562-
}
563-
}
485+
self.handle.wait().map(ExitStatus)
564486
}
565487

566488
/// Simultaneously waits for the child to exit and collect all remaining

0 commit comments

Comments
 (0)