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:
parent
c49d10207a
commit
2a345bbcc1
6 changed files with 30 additions and 32 deletions
|
@ -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
|
||||||
|
|
|
@ -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)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue