diff options
| author | bors <bors@rust-lang.org> | 2014-03-28 11:21:47 -0700 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2014-03-28 11:21:47 -0700 |
| commit | fd4f15ea6b04fcacc8c8446c5d725c25daab0624 (patch) | |
| tree | c77eb7ce115419a982d6f32f692b26f080b68ee9 /src/libnative | |
| parent | b8601a3d8b91ad3b653d143307611f2f5c75617e (diff) | |
| parent | 0e190b9a4ad92645e8f63c72c66cc89a596f1971 (diff) | |
| download | rust-fd4f15ea6b04fcacc8c8446c5d725c25daab0624.tar.gz rust-fd4f15ea6b04fcacc8c8446c5d725c25daab0624.zip | |
auto merge of #13131 : alexcrichton/rust/issue-13124, r=brson
It turns out that on linux, and possibly other platforms, child processes will continue to accept signals until they have been *reaped*. This means that once the child has exited, it will succeed to receive signals until waitpid() has been invoked on it. This is unfortunate behavior, and differs from what is seen on OSX and windows. This commit changes the behavior of Process::signal() to be the same across platforms, and updates the documentation of Process::kill() to note that when signaling a foreign process it may accept signals until reaped. Implementation-wise, this invokes waitpid() with WNOHANG before each signal to the child to ensure that if the child has exited that we will reap it. Other possibilities include installing a SIGCHLD signal handler, but at this time I believe that that's too complicated. Closes #13124
Diffstat (limited to 'src/libnative')
| -rw-r--r-- | src/libnative/io/process.rs | 81 |
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()), } } } |
