about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/libstd/process.rs42
-rw-r--r--src/libstd/sys/unix/process/process_unix.rs17
-rw-r--r--src/libstd/sys/windows/c.rs1
-rw-r--r--src/libstd/sys/windows/process.rs15
-rw-r--r--src/test/run-pass/try-wait.rs65
5 files changed, 140 insertions, 0 deletions
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<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.
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<ExitStatus> {
         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<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))
+        }
+    }
 }
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<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 }
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 <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());
+}