diff --git a/src/libstd/process.rs b/src/libstd/process.rs index 1a11d7ad9d7..aa76b792535 100644 --- a/src/libstd/process.rs +++ b/src/libstd/process.rs @@ -800,6 +800,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 { + 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. diff --git a/src/libstd/sys/unix/process/process_unix.rs b/src/libstd/sys/unix/process/process_unix.rs index aa426722025..0dc1739c1a1 100644 --- a/src/libstd/sys/unix/process/process_unix.rs +++ b/src/libstd/sys/unix/process/process_unix.rs @@ -237,6 +237,7 @@ impl Process { cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(|_| ()) } } + pub fn wait(&mut self) -> io::Result { use sys::cvt_r; if let Some(status) = self.status { @@ -247,4 +248,20 @@ impl Process { self.status = Some(ExitStatus::new(status)); Ok(ExitStatus::new(status)) } + + pub fn try_wait(&mut self) -> io::Result { + 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)) + } + } } diff --git a/src/libstd/sys/windows/c.rs b/src/libstd/sys/windows/c.rs index 910d802f059..dc7b2fc9a6b 100644 --- a/src/libstd/sys/windows/c.rs +++ b/src/libstd/sys/windows/c.rs @@ -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; diff --git a/src/libstd/sys/windows/process.rs b/src/libstd/sys/windows/process.rs index 7dc8959e1b6..d2ad81023e7 100644 --- a/src/libstd/sys/windows/process.rs +++ b/src/libstd/sys/windows/process.rs @@ -340,6 +340,21 @@ impl Process { } } + pub fn try_wait(&mut self) -> io::Result { + 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 } diff --git a/src/test/run-pass/try-wait.rs b/src/test/run-pass/try-wait.rs new file mode 100644 index 00000000000..fdaf0cfd5b0 --- /dev/null +++ b/src/test/run-pass/try-wait.rs @@ -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 or the MIT license +// , 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::>(); + 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()); +}