about summary refs log tree commit diff
path: root/src/libnative
diff options
context:
space:
mode:
Diffstat (limited to 'src/libnative')
-rw-r--r--src/libnative/io/process.rs81
1 files changed, 58 insertions, 23 deletions
diff --git a/src/libnative/io/process.rs b/src/libnative/io/process.rs
index 28f0817670b..463f9f8bedd 100644
--- a/src/libnative/io/process.rs
+++ b/src/libnative/io/process.rs
@@ -134,6 +134,18 @@ impl rtio::RtioProcess for Process {
     }
 
     fn kill(&mut self, signum: int) -> Result<(), io::IoError> {
+        // On linux (and possibly other unices), a process that has exited will
+        // continue to accept signals because it is "defunct". The delivery of
+        // signals will only fail once the child has been reaped. For this
+        // reason, if the process hasn't exited yet, then we attempt to collect
+        // their status with WNOHANG.
+        if self.exit_code.is_none() {
+            match waitpid_nowait(self.pid) {
+                Some(code) => { self.exit_code = Some(code); }
+                None => {}
+            }
+        }
+
         // if the process has finished, and therefore had waitpid called,
         // and we kill it, then on unix we might ending up killing a
         // newer process that happens to have the same (re-used) id
@@ -662,6 +674,31 @@ fn free_handle(_handle: *()) {
     // unix has no process handle object, just a pid
 }
 
+#[cfg(unix)]
+fn translate_status(status: c_int) -> p::ProcessExit {
+    #[cfg(target_os = "linux")]
+    #[cfg(target_os = "android")]
+    mod imp {
+        pub fn WIFEXITED(status: i32) -> bool { (status & 0xff) == 0 }
+        pub fn WEXITSTATUS(status: i32) -> i32 { (status >> 8) & 0xff }
+        pub fn WTERMSIG(status: i32) -> i32 { status & 0x7f }
+    }
+
+    #[cfg(target_os = "macos")]
+    #[cfg(target_os = "freebsd")]
+    mod imp {
+        pub fn WIFEXITED(status: i32) -> bool { (status & 0x7f) == 0 }
+        pub fn WEXITSTATUS(status: i32) -> i32 { status >> 8 }
+        pub fn WTERMSIG(status: i32) -> i32 { status & 0o177 }
+    }
+
+    if imp::WIFEXITED(status) {
+        p::ExitStatus(imp::WEXITSTATUS(status) as int)
+    } else {
+        p::ExitSignal(imp::WTERMSIG(status) as int)
+    }
+}
+
 /**
  * Waits for a process to exit and returns the exit code, failing
  * if there is no process with the specified id.
@@ -723,33 +760,31 @@ fn waitpid(pid: pid_t) -> p::ProcessExit {
     #[cfg(unix)]
     fn waitpid_os(pid: pid_t) -> p::ProcessExit {
         use std::libc::funcs::posix01::wait;
-
-        #[cfg(target_os = "linux")]
-        #[cfg(target_os = "android")]
-        mod imp {
-            pub fn WIFEXITED(status: i32) -> bool { (status & 0xff) == 0 }
-            pub fn WEXITSTATUS(status: i32) -> i32 { (status >> 8) & 0xff }
-            pub fn WTERMSIG(status: i32) -> i32 { status & 0x7f }
+        let mut status = 0 as c_int;
+        match retry(|| unsafe { wait::waitpid(pid, &mut status, 0) }) {
+            -1 => fail!("unknown waitpid error: {}", super::last_error()),
+            _ => translate_status(status),
         }
+    }
+}
 
-        #[cfg(target_os = "macos")]
-        #[cfg(target_os = "freebsd")]
-        mod imp {
-            pub fn WIFEXITED(status: i32) -> bool { (status & 0x7f) == 0 }
-            pub fn WEXITSTATUS(status: i32) -> i32 { status >> 8 }
-            pub fn WTERMSIG(status: i32) -> i32 { status & 0o177 }
-        }
+fn waitpid_nowait(pid: pid_t) -> Option<p::ProcessExit> {
+    return waitpid_os(pid);
 
+    // This code path isn't necessary on windows
+    #[cfg(windows)]
+    fn waitpid_os(_pid: pid_t) -> Option<p::ProcessExit> { None }
+
+    #[cfg(unix)]
+    fn waitpid_os(pid: pid_t) -> Option<p::ProcessExit> {
+        use std::libc::funcs::posix01::wait;
         let mut status = 0 as c_int;
-        match retry(|| unsafe { wait::waitpid(pid, &mut status, 0) }) {
-            -1 => fail!("unknown waitpid error: {}", super::last_error()),
-            _ => {
-                if imp::WIFEXITED(status) {
-                    p::ExitStatus(imp::WEXITSTATUS(status) as int)
-                } else {
-                    p::ExitSignal(imp::WTERMSIG(status) as int)
-                }
-            }
+        match retry(|| unsafe {
+            wait::waitpid(pid, &mut status, libc::WNOHANG)
+        }) {
+            n if n == pid => Some(translate_status(status)),
+            0 => None,
+            n => fail!("unknown waitpid error `{}`: {}", n, super::last_error()),
         }
     }
 }