Change:
calls on Unix, and on Windows. If process is terminated, but handle is alive yet, is successful on Unix, but fails on Windows. is successful on Unix because the process is zombie until waited (it fails after process , that's probably OK) According to So if someone else (not us) terminated the process, would fail. There are problems with this behavior: API is different between Unix and Windows API to reliably terminate the process is complicated call if failed, need to . And if is successful, ignore the error of I could reproduce it. I don't have easy access to Windows machine to create completely isolated test case, but repro looks like this: This fails with: exactly as described in WinAPI docs. I think proper implementation should look like this: if process has been waited already, fail explicitly, to be consistent with Unix call if failed, call if is successful and exit code is not , consider successful This is how is implemented in our project: <!-- If you're using the stable version of the compiler, you should also check if the bug also exists in the beta or nightly versions. --: </p</details
It does make sense to return success if the process has already exited. That said... I'm not sure this makes any sense when put in Windows terms. Wait is more like . There's no way to tell if something has been "waited". The best you can do is test if the process has exited already, which would make this step redundant given that the next steps test exactly that. Also it's important to note that is equal to 259 which is also a perfectly valid exit code. We should use instead. If we want to change this then my proposal would be to simply try to terminate and if that fails (with ?) then call to determine if the process has already exited. Also the docs should be updated to note the platform differences. It's very much just documenting POSIX atm.
Store a flag in , as Rust does for Unix. Good point — I don't know WinAPI well, but seems like this is how it is supposed to be implemented, thanks!
Perhaps. I admit I'm not terribly keen on trying to emulate the POSIX implementation exactly. This has lead to problems in the past, especially when it leads to inconsistent behaviour within the platform (for example, people using their own wait function(s) now have to know to also implement their own kill function). What is the benefit of erroring here? How would that error be handled?
Simplicity of writing cross-platform code. Write something on one OS, and it will behave the same on the other OS. Writing cross-platform code is hard because such corner cases needs to be handled properly, and which are not very easy to test (and even figure out what scenarios needs to be tested). That said, I'd prefer to change API on Unix instead to not return error on after the process has been waited. Because yes, how would that error be handled? What is benefit or erroring here?
Ok, I'm going to tag this for libs-api discussion. I'm personally uncertain here but I'd love to be convinced. I'll try to summarise below but also see the above discussion. The semantics of Windows and posix implementations are different. It is advantageous to smooth over those differences in the std. However, how to do that is the question. I think is wrong here. It says: The does not return and the only does so if (or maybe ) is called first. Aside from being inaccurately documented, I'm not really convinced this is useful behaviour. It is necessary for posix to attempt to avoid using a PID that may have been recycled, but to me this feels like a platform implementation detail ather than something for users to handle.
This was discussed in a libs-api meeting. The feeling was that it makes sense to simply return in the cases where the process has already exited. It will need a T-libs-api FCP though as this is a breaking change to the documented behaviour (even if the docs aren't quite accurate),

Discussion:
} impl Child { <del> /// Forces the child process to exit. If the child has already exited, an [`InvalidInput`] /// error is returned. </del> <ins> /// Forces the child process to exit. If the child has already exited, `Ok(())` /// is returned. </ins> /// /// The mapping to [`ErrorKind`]s is not part of the compatibility contract of the function. ///

/// /// let mut command = Command::new("yes"); /// if let Ok(mut child) = command.spawn() { <del> ///     child.kill().expect("command wasn't running"); </del> <ins> ///     child.kill().expect("command couldn't be killed"); </ins> /// } else { ///     println!("yes command didn't start"); /// }

assert!(output.status.success()); assert_eq!(String::from_utf8_lossy(&output.stdout).trim(), "Hello, fellow Rustaceans!"); } <ins> #[test] fn terminate_exited_process() { let mut cmd = if cfg!(target_os = "android") { let mut p = shell_cmd(); p.args(&["-c", "true"]); p } else { known_command() }; let mut p = cmd.stdout(Stdio::null()).spawn().unwrap(); p.wait().unwrap(); assert!(p.kill().is_ok()); assert!(p.kill().is_ok()); } </ins>

pub fn kill(&mut self) -> io::Result<()> { // If we've already waited on this process then the pid can be recycled // and used for another process, and we probably shouldn't be killing <del> // random processes, so just return an error. </del> <ins> // random processes, so return Ok because the process has exited already. </ins> if self.status.is_some() { <del> Err(io::const_io_error!( ErrorKind::InvalidInput, "invalid argument: can't kill an exited process", )) </del> <ins> Ok(()) </ins> } else { cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(drop) }

impl Process { pub fn kill(&mut self) -> io::Result<()> { <del> cvt(unsafe { c::TerminateProcess(self.handle.as_raw_handle(), 1) })?; </del> <ins> let result = unsafe { c::TerminateProcess(self.handle.as_raw_handle(), 1) }; if result == c::FALSE { let error = unsafe { c::GetLastError() }; // TerminateProcess returns ERROR_ACCESS_DENIED if the process has already been // terminated (by us, or for any other reason). So check if the process was actually // terminated, and if so, do not return an error. if error != c::ERROR_ACCESS_DENIED || self.try_wait().is_err() { return Err(crate::io::Error::from_raw_os_error(error as i32)); } } </ins> Ok(()) }