Skip to content

std::process::Child::kill should not fail if process is terminated on Windows #112423

Closed
@stepancheg

Description

@stepancheg

Child::kill calls kill on Unix, and TerminateProcess on Windows.

If process is terminated, but handle is alive yet, kill is successful on Unix, but fails on Windows.

kill is successful on Unix because the process is zombie until waited (it fails after process waited, that's probably OK)

According to documentation of TerminateProcess

After a process has terminated, call to TerminateProcess with open handles to the process fails with ERROR_ACCESS_DENIED (5) error code

So if someone else (not us) terminated the process, kill would fail.

There are problems with this behavior:

  • API is different between Unix and Windows
  • API to reliably terminate the process is complicated
    • call kill
    • if kill failed, need to try_wait. And if try_wait is successful, ignore the error of kill

I could reproduce it. I don't have easy access to Windows machine to create completely isolated test case, but repro looks like this:

let mut command = std::process::Command::new("powershell");
command.args(["-c", "Start-Sleep -Seconds 10000"]);
let mut child = command.spawn().unwrap();
let pid = child.id().try_into().unwrap();

// Terminate by process id, NOT by our process handle. Like if some other process killed it.
// This function calls `OpenProcess(pid)` followed by `TerminateProcess`.
kill(pid).unwrap();

thread::sleep(Duration::from_secs(5));

child.kill().expect("Failed to kill child process");

This fails with:

Failed to kill child process: Os { code: 5, kind: PermissionDenied, message: "Access is denied." }

exactly as described in WinAPI docs.

I think proper kill implementation should look like this:

  • if process has been waited already, fail explicitly, to be consistent with Unix
  • call TerminateProcess
  • if TerminateProcess failed, call GetExitCodeProcess
  • if GetExitCodeProcess is successful and exit code is not STILL_ACTIVE, consider kill successful

This is how kill is implemented in our project:

https://github.com/facebook/buck2/blob/b6fb1364e1caf6dac821767b27bda65726e38895/app/buck2_wrapper_common/src/winapi_process.rs#L58-L72

Meta

rustc --version --verbose:

rustc 1.70.0-nightly

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-bugCategory: This is a bug.O-windowsOperating system: WindowsT-libs-apiRelevant to the library API team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions