Skip to content

Commit 627515a

Browse files
committed
std: Push Child's exit status to sys::process
On Unix we have to be careful to not call `waitpid` twice, but we don't have to be careful on Windows due to the way process handles work there. As a result the cached `Option<ExitStatus>` is only necessary on Unix, and it's also just an implementation detail of the Unix module. At the same time. also update some code in `kill` on Unix to avoid a wonky waitpid with WNOHANG. This was added in 0e190b9 to solve rust-lang#13124, but the `signal(0)` method is not supported any more so there's no need to for this workaround. I believe that this is no longer necessary as it's not really doing anything.
1 parent b1898db commit 627515a

File tree

3 files changed

+30
-69
lines changed

3 files changed

+30
-69
lines changed

src/libstd/process.rs

+5-44
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,14 @@
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;
2423
use sys::pipe::{self, AnonPipe};
2524
use sys::process as imp;
@@ -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>,
@@ -243,7 +239,7 @@ impl Command {
243239

244240
/// Sets the working directory for the child process.
245241
#[stable(feature = "process", since = "1.0.0")]
246-
pub fn current_dir<P: AsRef<path::Path>>(&mut self, dir: P) -> &mut Command {
242+
pub fn current_dir<P: AsRef<Path>>(&mut self, dir: P) -> &mut Command {
247243
self.inner.cwd(dir.as_ref().as_ref());
248244
self
249245
}
@@ -288,7 +284,6 @@ impl Command {
288284
Err(e) => Err(e),
289285
Ok(handle) => Ok(Child {
290286
handle: handle,
291-
status: None,
292287
stdin: our_stdin.map(|fd| ChildStdin { inner: fd }),
293288
stdout: our_stdout.map(|fd| ChildStdout { inner: fd }),
294289
stderr: our_stderr.map(|fd| ChildStderr { inner: fd }),
@@ -508,34 +503,7 @@ impl Child {
508503
/// SIGKILL on unix platforms.
509504
#[stable(feature = "process", since = "1.0.0")]
510505
pub fn kill(&mut self) -> io::Result<()> {
511-
#[cfg(unix)] fn collect_status(p: &mut Child) {
512-
// On Linux (and possibly other unices), a process that has exited will
513-
// continue to accept signals because it is "defunct". The delivery of
514-
// signals will only fail once the child has been reaped. For this
515-
// reason, if the process hasn't exited yet, then we attempt to collect
516-
// their status with WNOHANG.
517-
if p.status.is_none() {
518-
match p.handle.try_wait() {
519-
Some(status) => { p.status = Some(status); }
520-
None => {}
521-
}
522-
}
523-
}
524-
#[cfg(windows)] fn collect_status(_p: &mut Child) {}
525-
526-
collect_status(self);
527-
528-
// if the process has finished, and therefore had waitpid called,
529-
// and we kill it, then on unix we might ending up killing a
530-
// newer process that happens to have the same (re-used) id
531-
if self.status.is_some() {
532-
return Err(Error::new(
533-
ErrorKind::InvalidInput,
534-
"invalid argument: can't kill an exited process",
535-
))
536-
}
537-
538-
unsafe { self.handle.kill() }
506+
self.handle.kill()
539507
}
540508

541509
/// Returns the OS-assigned process identifier associated with this child.
@@ -555,14 +523,7 @@ impl Child {
555523
#[stable(feature = "process", since = "1.0.0")]
556524
pub fn wait(&mut self) -> io::Result<ExitStatus> {
557525
drop(self.stdin.take());
558-
match self.status {
559-
Some(code) => Ok(ExitStatus(code)),
560-
None => {
561-
let status = try!(self.handle.wait());
562-
self.status = Some(status);
563-
Ok(ExitStatus(status))
564-
}
565-
}
526+
self.handle.wait().map(ExitStatus)
566527
}
567528

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

src/libstd/sys/unix/process.rs

+20-22
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
#![allow(non_snake_case)]
12-
1311
use prelude::v1::*;
1412
use os::unix::prelude::*;
1513

@@ -271,7 +269,8 @@ impl fmt::Display for ExitStatus {
271269

272270
/// The unique id of the process (this should never be negative).
273271
pub struct Process {
274-
pid: pid_t
272+
pid: pid_t,
273+
status: Option<ExitStatus>,
275274
}
276275

277276
pub enum Stdio {
@@ -285,11 +284,6 @@ pub type RawStdio = FileDesc;
285284
const CLOEXEC_MSG_FOOTER: &'static [u8] = b"NOEX";
286285

287286
impl Process {
288-
pub unsafe fn kill(&self) -> io::Result<()> {
289-
try!(cvt(libc::kill(self.pid, libc::SIGKILL)));
290-
Ok(())
291-
}
292-
293287
pub fn spawn(cfg: &mut Command,
294288
in_fd: Stdio,
295289
out_fd: Stdio,
@@ -324,7 +318,7 @@ impl Process {
324318
}
325319
};
326320

327-
let p = Process{ pid: pid };
321+
let mut p = Process { pid: pid, status: None };
328322
drop(output);
329323
let mut bytes = [0; 8];
330324

@@ -516,22 +510,26 @@ impl Process {
516510
self.pid as u32
517511
}
518512

519-
pub fn wait(&self) -> io::Result<ExitStatus> {
520-
let mut status = 0 as c_int;
521-
try!(cvt_r(|| unsafe { libc::waitpid(self.pid, &mut status, 0) }));
522-
Ok(ExitStatus(status))
513+
pub fn kill(&mut self) -> io::Result<()> {
514+
// If we've already waited on this process then the pid can be recycled
515+
// and used for another process, and we probably shouldn't be killing
516+
// random processes, so just return an error.
517+
if self.status.is_some() {
518+
Err(Error::new(ErrorKind::InvalidInput,
519+
"invalid argument: can't kill an exited process"))
520+
} else {
521+
cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(|_| ())
522+
}
523523
}
524524

525-
pub fn try_wait(&self) -> Option<ExitStatus> {
526-
let mut status = 0 as c_int;
527-
match cvt_r(|| unsafe {
528-
libc::waitpid(self.pid, &mut status, libc::WNOHANG)
529-
}) {
530-
Ok(0) => None,
531-
Ok(n) if n == self.pid => Some(ExitStatus(status)),
532-
Ok(n) => panic!("unknown pid: {}", n),
533-
Err(e) => panic!("unknown waitpid error: {}", e),
525+
pub fn wait(&mut self) -> io::Result<ExitStatus> {
526+
if let Some(status) = self.status {
527+
return Ok(status)
534528
}
529+
let mut status = 0 as c_int;
530+
try!(cvt_r(|| unsafe { libc::waitpid(self.pid, &mut status, 0) }));
531+
self.status = Some(ExitStatus(status));
532+
Ok(ExitStatus(status))
535533
}
536534
}
537535

src/libstd/sys/windows/process.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -202,8 +202,10 @@ impl Process {
202202
Ok(Process { handle: Handle::new(pi.hProcess) })
203203
}
204204

205-
pub unsafe fn kill(&self) -> io::Result<()> {
206-
try!(cvt(c::TerminateProcess(self.handle.raw(), 1)));
205+
pub fn kill(&mut self) -> io::Result<()> {
206+
try!(cvt(unsafe {
207+
c::TerminateProcess(self.handle.raw(), 1)
208+
}));
207209
Ok(())
208210
}
209211

@@ -213,7 +215,7 @@ impl Process {
213215
}
214216
}
215217

216-
pub fn wait(&self) -> io::Result<ExitStatus> {
218+
pub fn wait(&mut self) -> io::Result<ExitStatus> {
217219
unsafe {
218220
let res = c::WaitForSingleObject(self.handle.raw(), c::INFINITE);
219221
if res != c::WAIT_OBJECT_0 {

0 commit comments

Comments
 (0)