1
Fork 0

make Child::try_wait return io::Result<Option<ExitStatus>>

This is much nicer for callers who want to short-circuit real I/O errors
with `?`, because they can write this

    if let Some(status) = foo.try_wait()? {
        ...
    } else {
        ...
    }

instead of this

    match foo.try_wait() {
        Ok(status) => {
            ...
        }
        Err(err) if err.kind() == io::ErrorKind::WouldBlock => {
            ...
        }
        Err(err) => return Err(err),
    }

The original design of `try_wait` was patterned after the `Read` and
`Write` traits, which support both blocking and non-blocking
implementations in a single API. But since `try_wait` is never blocking,
it makes sense to optimize for the non-blocking case.

Tracking issue: https://github.com/rust-lang/rust/issues/38903
This commit is contained in:
Jack O'Connor 2017-02-03 17:39:41 -05:00
parent c49d10207a
commit 2a345bbcc1
6 changed files with 30 additions and 32 deletions

View file

@ -844,9 +844,9 @@ impl Child {
/// guaranteed to repeatedly return a successful exit status so long as the /// guaranteed to repeatedly return a successful exit status so long as the
/// child has already exited. /// child has already exited.
/// ///
/// If the child has exited, then `Ok(status)` is returned. If the exit /// If the child has exited, then `Ok(Some(status))` is returned. If the
/// status is not available at this time then an error is returned with the /// exit status is not available at this time then `Ok(None)` is returned.
/// error kind `WouldBlock`. If an error occurs, then that error is returned. /// If an error occurs, then that error is returned.
/// ///
/// Note that unlike `wait`, this function will not attempt to drop stdin. /// Note that unlike `wait`, this function will not attempt to drop stdin.
/// ///
@ -857,14 +857,13 @@ impl Child {
/// ```no_run /// ```no_run
/// #![feature(process_try_wait)] /// #![feature(process_try_wait)]
/// ///
/// use std::io;
/// use std::process::Command; /// use std::process::Command;
/// ///
/// let mut child = Command::new("ls").spawn().unwrap(); /// let mut child = Command::new("ls").spawn().unwrap();
/// ///
/// match child.try_wait() { /// match child.try_wait() {
/// Ok(status) => println!("exited with: {}", status), /// Ok(Some(status)) => println!("exited with: {}", status),
/// Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { /// Ok(None) => {
/// println!("status not ready yet, let's really wait"); /// println!("status not ready yet, let's really wait");
/// let res = child.wait(); /// let res = child.wait();
/// println!("result: {:?}", res); /// println!("result: {:?}", res);
@ -873,8 +872,8 @@ impl Child {
/// } /// }
/// ``` /// ```
#[unstable(feature = "process_try_wait", issue = "38903")] #[unstable(feature = "process_try_wait", issue = "38903")]
pub fn try_wait(&mut self) -> io::Result<ExitStatus> { pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
self.handle.try_wait().map(ExitStatus) Ok(self.handle.try_wait()?.map(ExitStatus))
} }
/// Simultaneously waits for the child to exit and collect all remaining /// Simultaneously waits for the child to exit and collect all remaining

View file

@ -502,17 +502,17 @@ impl Process {
Ok(ExitStatus(status as i32)) Ok(ExitStatus(status as i32))
} }
pub fn try_wait(&mut self) -> io::Result<ExitStatus> { pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
if let Some(status) = self.status { if let Some(status) = self.status {
return Ok(status) return Ok(Some(status))
} }
let mut status = 0; let mut status = 0;
let pid = cvt(syscall::waitpid(self.pid, &mut status, syscall::WNOHANG))?; let pid = cvt(syscall::waitpid(self.pid, &mut status, syscall::WNOHANG))?;
if pid == 0 { if pid == 0 {
Err(io::Error::from_raw_os_error(syscall::EWOULDBLOCK)) Ok(None)
} else { } else {
self.status = Some(ExitStatus(status as i32)); self.status = Some(ExitStatus(status as i32));
Ok(ExitStatus(status as i32)) Ok(Some(ExitStatus(status as i32)))
} }
} }
} }

View file

@ -165,7 +165,7 @@ impl Process {
Ok(ExitStatus::new(proc_info.rec.return_code)) Ok(ExitStatus::new(proc_info.rec.return_code))
} }
pub fn try_wait(&mut self) -> io::Result<ExitStatus> { pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
use default::Default; use default::Default;
use sys::process::magenta::*; use sys::process::magenta::*;
@ -179,7 +179,7 @@ impl Process {
match status { match status {
0 => { }, // Success 0 => { }, // Success
x if x == ERR_TIMED_OUT => { x if x == ERR_TIMED_OUT => {
return Err(io::Error::from(io::ErrorKind::WouldBlock)); return Ok(None);
}, },
_ => { panic!("Failed to wait on process handle: {}", status); }, _ => { panic!("Failed to wait on process handle: {}", status); },
} }
@ -192,7 +192,7 @@ impl Process {
return Err(io::Error::new(io::ErrorKind::InvalidData, return Err(io::Error::new(io::ErrorKind::InvalidData,
"Failed to get exit status of process")); "Failed to get exit status of process"));
} }
Ok(ExitStatus::new(proc_info.rec.return_code)) Ok(Some(ExitStatus::new(proc_info.rec.return_code)))
} }
} }

View file

@ -249,19 +249,19 @@ impl Process {
Ok(ExitStatus::new(status)) Ok(ExitStatus::new(status))
} }
pub fn try_wait(&mut self) -> io::Result<ExitStatus> { pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
if let Some(status) = self.status { if let Some(status) = self.status {
return Ok(status) return Ok(Some(status))
} }
let mut status = 0 as c_int; let mut status = 0 as c_int;
let pid = cvt(unsafe { let pid = cvt(unsafe {
libc::waitpid(self.pid, &mut status, libc::WNOHANG) libc::waitpid(self.pid, &mut status, libc::WNOHANG)
})?; })?;
if pid == 0 { if pid == 0 {
Err(io::Error::from_raw_os_error(libc::EWOULDBLOCK)) Ok(None)
} else { } else {
self.status = Some(ExitStatus::new(status)); self.status = Some(ExitStatus::new(status));
Ok(ExitStatus::new(status)) Ok(Some(ExitStatus::new(status)))
} }
} }
} }

View file

@ -340,18 +340,18 @@ impl Process {
} }
} }
pub fn try_wait(&mut self) -> io::Result<ExitStatus> { pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
unsafe { unsafe {
match c::WaitForSingleObject(self.handle.raw(), 0) { match c::WaitForSingleObject(self.handle.raw(), 0) {
c::WAIT_OBJECT_0 => {} c::WAIT_OBJECT_0 => {}
c::WAIT_TIMEOUT => { c::WAIT_TIMEOUT => {
return Err(io::Error::from_raw_os_error(c::WSAEWOULDBLOCK)) return Ok(None);
} }
_ => return Err(io::Error::last_os_error()), _ => return Err(io::Error::last_os_error()),
} }
let mut status = 0; let mut status = 0;
cvt(c::GetExitCodeProcess(self.handle.raw(), &mut status))?; cvt(c::GetExitCodeProcess(self.handle.raw(), &mut status))?;
Ok(ExitStatus(status)) Ok(Some(ExitStatus(status)))
} }
} }

View file

@ -13,7 +13,6 @@
#![feature(process_try_wait)] #![feature(process_try_wait)]
use std::env; use std::env;
use std::io;
use std::process::Command; use std::process::Command;
use std::thread; use std::thread;
use std::time::Duration; use std::time::Duration;
@ -32,17 +31,17 @@ fn main() {
.arg("sleep") .arg("sleep")
.spawn() .spawn()
.unwrap(); .unwrap();
let err = me.try_wait().unwrap_err(); let maybe_status = me.try_wait().unwrap();
assert_eq!(err.kind(), io::ErrorKind::WouldBlock); assert!(maybe_status.is_none());
let err = me.try_wait().unwrap_err(); let maybe_status = me.try_wait().unwrap();
assert_eq!(err.kind(), io::ErrorKind::WouldBlock); assert!(maybe_status.is_none());
me.kill().unwrap(); me.kill().unwrap();
me.wait().unwrap(); me.wait().unwrap();
let status = me.try_wait().unwrap(); let status = me.try_wait().unwrap().unwrap();
assert!(!status.success()); assert!(!status.success());
let status = me.try_wait().unwrap(); let status = me.try_wait().unwrap().unwrap();
assert!(!status.success()); assert!(!status.success());
let mut me = Command::new(env::current_exe().unwrap()) let mut me = Command::new(env::current_exe().unwrap())
@ -51,17 +50,17 @@ fn main() {
.unwrap(); .unwrap();
loop { loop {
match me.try_wait() { match me.try_wait() {
Ok(res) => { Ok(Some(res)) => {
assert!(res.success()); assert!(res.success());
break break
} }
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { Ok(None) => {
thread::sleep(Duration::from_millis(1)); thread::sleep(Duration::from_millis(1));
} }
Err(e) => panic!("error in try_wait: {}", e), Err(e) => panic!("error in try_wait: {}", e),
} }
} }
let status = me.try_wait().unwrap(); let status = me.try_wait().unwrap().unwrap();
assert!(status.success()); assert!(status.success());
} }