Skip to content

std: Add a nonblocking Child::try_wait method #38866

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 9, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions src/libstd/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -793,6 +793,48 @@ impl Child {
self.handle.wait().map(ExitStatus)
}

/// Attempts to collect the exit status of the child if it has already
/// exited.
///
/// This function will not block the calling thread and will only advisorily
/// check to see if the child process has exited or not. If the child has
/// exited then on Unix the process id is reaped. This function is
/// guaranteed to repeatedly return a successful exit status so long as the
/// child has already exited.
///
/// If the child has exited, then `Ok(status)` is returned. If the exit
/// status is not available at this time then an error is returned with the
/// error kind `WouldBlock`. If an error occurs, then that error is returned.
///
/// Note that unlike `wait`, this function will not attempt to drop stdin.
///
/// # Examples
///
/// Basic usage:
///
/// ```no_run
/// #![feature(process_try_wait)]
///
/// use std::io;
/// use std::process::Command;
///
/// let mut child = Command::new("ls").spawn().unwrap();
///
/// match child.try_wait() {
/// Ok(status) => println!("exited with: {}", status),
/// Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
/// println!("status not ready yet, let's really wait");
/// let res = child.wait();
/// println!("result: {:?}", res);
/// }
/// Err(e) => println!("error attempting to wait: {}", e),
/// }
/// ```
#[unstable(feature = "process_try_wait", issue = "38903")]
pub fn try_wait(&mut self) -> io::Result<ExitStatus> {
self.handle.try_wait().map(ExitStatus)
}

/// Simultaneously waits for the child to exit and collect all remaining
/// output on the stdout/stderr handles, returning an `Output`
/// instance.
Expand Down
17 changes: 17 additions & 0 deletions src/libstd/sys/unix/process/process_unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ impl Process {
cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(|_| ())
}
}

pub fn wait(&mut self) -> io::Result<ExitStatus> {
use sys::cvt_r;
if let Some(status) = self.status {
Expand All @@ -247,4 +248,20 @@ impl Process {
self.status = Some(ExitStatus::new(status));
Ok(ExitStatus::new(status))
}

pub fn try_wait(&mut self) -> io::Result<ExitStatus> {
if let Some(status) = self.status {
return Ok(status)
}
let mut status = 0 as c_int;
let pid = cvt(unsafe {
libc::waitpid(self.pid, &mut status, libc::WNOHANG)
})?;
if pid == 0 {
Err(io::Error::from_raw_os_error(libc::EWOULDBLOCK))
} else {
self.status = Some(ExitStatus::new(status));
Ok(ExitStatus::new(status))
}
}
}
1 change: 1 addition & 0 deletions src/libstd/sys/windows/c.rs
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@ pub const FILE_CURRENT: DWORD = 1;
pub const FILE_END: DWORD = 2;

pub const WAIT_OBJECT_0: DWORD = 0x00000000;
pub const WAIT_TIMEOUT: DWORD = 258;

#[cfg(target_env = "msvc")]
pub const MAX_SYM_NAME: usize = 2000;
Expand Down
15 changes: 15 additions & 0 deletions src/libstd/sys/windows/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,21 @@ impl Process {
}
}

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

pub fn handle(&self) -> &Handle { &self.handle }

pub fn into_handle(self) -> Handle { self.handle }
Expand Down
65 changes: 65 additions & 0 deletions src/test/run-pass/try-wait.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![feature(process_try_wait)]

use std::env;
use std::io;
use std::process::Command;
use std::thread;
use std::time::Duration;

fn main() {
let args = env::args().collect::<Vec<_>>();
if args.len() != 1 {
match &args[1][..] {
"sleep" => thread::sleep(Duration::new(1_000, 0)),
_ => {}
}
return
}

let mut me = Command::new(env::current_exe().unwrap())
.arg("sleep")
.spawn()
.unwrap();
let err = me.try_wait().unwrap_err();
assert_eq!(err.kind(), io::ErrorKind::WouldBlock);
let err = me.try_wait().unwrap_err();
assert_eq!(err.kind(), io::ErrorKind::WouldBlock);

me.kill().unwrap();
me.wait().unwrap();

let status = me.try_wait().unwrap();
assert!(!status.success());
let status = me.try_wait().unwrap();
assert!(!status.success());

let mut me = Command::new(env::current_exe().unwrap())
.arg("return-quickly")
.spawn()
.unwrap();
loop {
match me.try_wait() {
Ok(res) => {
assert!(res.success());
break
}
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
thread::sleep(Duration::from_millis(1));
}
Err(e) => panic!("error in try_wait: {}", e),
}
}

let status = me.try_wait().unwrap();
assert!(status.success());
}