Skip to content

Commit 6dbd250

Browse files
committed
Auto merge of #29462 - alexcrichton:refactor-process-ret, r=aturon
* Store the native representation directly in the `ExitStatus` structure instead of a "parsed version" (mostly for Unix). * On Windows, be more robust against processes exiting with the status of 259. Unfortunately this exit code corresponds to `STILL_ACTIVE`, causing libstd to think the process was still alive, causing an infinite loop. Instead the loop is removed altogether and `WaitForSingleObject` is used to wait for the process to exit.
2 parents 475f91f + 94aee5b commit 6dbd250

File tree

4 files changed

+94
-64
lines changed

4 files changed

+94
-64
lines changed

src/libstd/sys/unix/ext/process.rs

+1-4
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,7 @@ pub trait ExitStatusExt {
7575
#[stable(feature = "rust1", since = "1.0.0")]
7676
impl ExitStatusExt for process::ExitStatus {
7777
fn signal(&self) -> Option<i32> {
78-
match *self.as_inner() {
79-
sys::process::ExitStatus::Signal(s) => Some(s),
80-
_ => None
81-
}
78+
self.as_inner().signal()
8279
}
8380
}
8481

src/libstd/sys/unix/process.rs

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

11+
#![allow(non_snake_case)]
12+
1113
use prelude::v1::*;
1214
use os::unix::prelude::*;
1315

@@ -84,33 +86,62 @@ impl Command {
8486

8587
/// Unix exit statuses
8688
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
87-
pub enum ExitStatus {
88-
/// Normal termination with an exit code.
89-
Code(i32),
90-
91-
/// Termination by signal, with the signal number.
92-
///
93-
/// Never generated on Windows.
94-
Signal(i32),
89+
pub struct ExitStatus(c_int);
90+
91+
#[cfg(any(target_os = "linux", target_os = "android",
92+
target_os = "nacl"))]
93+
mod status_imp {
94+
pub fn WIFEXITED(status: i32) -> bool { (status & 0xff) == 0 }
95+
pub fn WEXITSTATUS(status: i32) -> i32 { (status >> 8) & 0xff }
96+
pub fn WTERMSIG(status: i32) -> i32 { status & 0x7f }
97+
}
98+
99+
#[cfg(any(target_os = "macos",
100+
target_os = "ios",
101+
target_os = "freebsd",
102+
target_os = "dragonfly",
103+
target_os = "bitrig",
104+
target_os = "netbsd",
105+
target_os = "openbsd"))]
106+
mod status_imp {
107+
pub fn WIFEXITED(status: i32) -> bool { (status & 0x7f) == 0 }
108+
pub fn WEXITSTATUS(status: i32) -> i32 { status >> 8 }
109+
pub fn WTERMSIG(status: i32) -> i32 { status & 0o177 }
95110
}
96111

97112
impl ExitStatus {
113+
fn exited(&self) -> bool {
114+
status_imp::WIFEXITED(self.0)
115+
}
116+
98117
pub fn success(&self) -> bool {
99-
*self == ExitStatus::Code(0)
118+
self.code() == Some(0)
100119
}
120+
101121
pub fn code(&self) -> Option<i32> {
102-
match *self {
103-
ExitStatus::Code(c) => Some(c),
104-
_ => None
122+
if self.exited() {
123+
Some(status_imp::WEXITSTATUS(self.0))
124+
} else {
125+
None
126+
}
127+
}
128+
129+
pub fn signal(&self) -> Option<i32> {
130+
if !self.exited() {
131+
Some(status_imp::WTERMSIG(self.0))
132+
} else {
133+
None
105134
}
106135
}
107136
}
108137

109138
impl fmt::Display for ExitStatus {
110139
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
111-
match *self {
112-
ExitStatus::Code(code) => write!(f, "exit code: {}", code),
113-
ExitStatus::Signal(code) => write!(f, "signal: {}", code),
140+
if let Some(code) = self.code() {
141+
write!(f, "exit code: {}", code)
142+
} else {
143+
let signal = self.signal().unwrap();
144+
write!(f, "signal: {}", signal)
114145
}
115146
}
116147
}
@@ -351,7 +382,7 @@ impl Process {
351382
pub fn wait(&self) -> io::Result<ExitStatus> {
352383
let mut status = 0 as c_int;
353384
try!(cvt_r(|| unsafe { c::waitpid(self.pid, &mut status, 0) }));
354-
Ok(translate_status(status))
385+
Ok(ExitStatus(status))
355386
}
356387

357388
pub fn try_wait(&self) -> Option<ExitStatus> {
@@ -360,7 +391,7 @@ impl Process {
360391
c::waitpid(self.pid, &mut status, c::WNOHANG)
361392
}) {
362393
Ok(0) => None,
363-
Ok(n) if n == self.pid => Some(translate_status(status)),
394+
Ok(n) if n == self.pid => Some(ExitStatus(status)),
364395
Ok(n) => panic!("unknown pid: {}", n),
365396
Err(e) => panic!("unknown waitpid error: {}", e),
366397
}
@@ -418,36 +449,6 @@ fn make_envp(env: Option<&HashMap<OsString, OsString>>)
418449
}
419450
}
420451

421-
fn translate_status(status: c_int) -> ExitStatus {
422-
#![allow(non_snake_case)]
423-
#[cfg(any(target_os = "linux", target_os = "android",
424-
target_os = "nacl"))]
425-
mod imp {
426-
pub fn WIFEXITED(status: i32) -> bool { (status & 0xff) == 0 }
427-
pub fn WEXITSTATUS(status: i32) -> i32 { (status >> 8) & 0xff }
428-
pub fn WTERMSIG(status: i32) -> i32 { status & 0x7f }
429-
}
430-
431-
#[cfg(any(target_os = "macos",
432-
target_os = "ios",
433-
target_os = "freebsd",
434-
target_os = "dragonfly",
435-
target_os = "bitrig",
436-
target_os = "netbsd",
437-
target_os = "openbsd"))]
438-
mod imp {
439-
pub fn WIFEXITED(status: i32) -> bool { (status & 0x7f) == 0 }
440-
pub fn WEXITSTATUS(status: i32) -> i32 { status >> 8 }
441-
pub fn WTERMSIG(status: i32) -> i32 { status & 0o177 }
442-
}
443-
444-
if imp::WIFEXITED(status) {
445-
ExitStatus::Code(imp::WEXITSTATUS(status))
446-
} else {
447-
ExitStatus::Signal(imp::WTERMSIG(status))
448-
}
449-
}
450-
451452
#[cfg(test)]
452453
mod tests {
453454
use super::*;

src/libstd/sys/windows/process.rs

+8-13
Original file line numberDiff line numberDiff line change
@@ -201,21 +201,16 @@ impl Process {
201201
}
202202

203203
pub fn wait(&self) -> io::Result<ExitStatus> {
204-
use libc::{STILL_ACTIVE, INFINITE, WAIT_OBJECT_0};
204+
use libc::{INFINITE, WAIT_OBJECT_0};
205205
use libc::{GetExitCodeProcess, WaitForSingleObject};
206206

207207
unsafe {
208-
loop {
209-
let mut status = 0;
210-
try!(cvt(GetExitCodeProcess(self.handle.raw(), &mut status)));
211-
if status != STILL_ACTIVE {
212-
return Ok(ExitStatus(status as i32));
213-
}
214-
match WaitForSingleObject(self.handle.raw(), INFINITE) {
215-
WAIT_OBJECT_0 => {}
216-
_ => return Err(Error::last_os_error()),
217-
}
208+
if WaitForSingleObject(self.handle.raw(), INFINITE) != WAIT_OBJECT_0 {
209+
return Err(Error::last_os_error())
218210
}
211+
let mut status = 0;
212+
try!(cvt(GetExitCodeProcess(self.handle.raw(), &mut status)));
213+
Ok(ExitStatus(status))
219214
}
220215
}
221216

@@ -225,14 +220,14 @@ impl Process {
225220
}
226221

227222
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
228-
pub struct ExitStatus(i32);
223+
pub struct ExitStatus(libc::DWORD);
229224

230225
impl ExitStatus {
231226
pub fn success(&self) -> bool {
232227
self.0 == 0
233228
}
234229
pub fn code(&self) -> Option<i32> {
235-
Some(self.0)
230+
Some(self.0 as i32)
236231
}
237232
}
238233

src/test/run-pass/weird-exit-code.rs

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// On Windows the GetExitCodeProcess API is used to get the exit code of a
12+
// process, but it's easy to mistake a process exiting with the code 259 as
13+
// "still running" because this is the value of the STILL_ACTIVE constant. Make
14+
// sure we handle this case in the standard library and correctly report the
15+
// status.
16+
//
17+
// Note that this is disabled on unix as processes exiting with 259 will have
18+
// their exit status truncated to 3 (only the lower 8 bits are used).
19+
20+
use std::process::{self, Command};
21+
use std::env;
22+
23+
fn main() {
24+
if !cfg!(windows) {
25+
return
26+
}
27+
28+
if env::args().len() == 1 {
29+
let status = Command::new(env::current_exe().unwrap())
30+
.arg("foo")
31+
.status()
32+
.unwrap();
33+
assert_eq!(status.code(), Some(259));
34+
} else {
35+
process::exit(259);
36+
}
37+
}

0 commit comments

Comments
 (0)