Skip to content

win32unix: cannot kill and wait for arbitrary processes #11021

@MisterDA

Description

@MisterDA

Hi! I encountered this issue writing test cases for my PRs on
win32unix.

This issue is closely related to #4034. As a result of pids in
win32unix being so-called "pseudo-pids" (handles casted to integers),
they're local to each process. As @xavierleroy explained there,

if Unix.waitpid is called for a process that has already
terminated, its processID is invalid (Windows doesn't keep the
process around if its handles are closed) and there is no way to
recover its handle and therefore its exit code.

Adrawback is that with the current API processes can only wait or
kill processes they've created themselves and not arbitrary processes,
since there's currently no way to get a handle to arbitrary
processes. Furthermore, if a "real" pid (regardless of whether the
corresponding process was created in the same "parent" process or
elsewhere) is given to Unix.waitpid or Unix.kill, the functions
will raise Unix.ESRCH.

For instance, the following program fails and cannot be correctly
written with the current API (it is to an extent pseudo-code):

Grand-parent tries to kill grand-child
let parent () =
  let null = Unix.openfile Filename.null [Unix.O_RDWR; O_CLOEXEC] 0o0 in
  let pin, pout = Unix.pipe ~cloexec:true () in

  let child = Unix.create_process Sys.argv.(0) [|Sys.argv.(0); "child"|]
                null pout Unix.stderr in

  let buf = Bytes.create 8 in
  assert (Unix.read pin buf 0 8 = 8);
  let sub_child = Bytes.get_int64_ne buf 0 |> Int64.to_int in

  begin try
      Unix.kill child Sys.sigkill;
      print_endline "PARENT: killed child process."
    with Unix.Unix_error(Unix.ESRCH, _, _) ->
      print_endline "PARENT: couldn't kill child process."
  end;
  begin try
      Unix.kill sub_child Sys.sigkill;
      print_endline "PARENT: killed sub_child process."
    with Unix.Unix_error(Unix.ESRCH, _, _) ->
      print_endline "PARENT: couldn't kill sub_child process."
  end

let child () =
  let _ = Unix.create_process Sys.argv.(0) [|Sys.argv.(0); "subchild"|]
                    Unix.stdin Unix.stdout Unix.stderr in
  Unix.sleep 2

let sub_child () =
  let pid = Unix.getpid () in
  let buf = Bytes.create 8 in
  Bytes.set_int64_ne buf 0 (Int64.of_int pid);
  assert (Unix.write Unix.stdout buf 0 8 = 8);
  while true do Unix.sleep 2 done

let () =
  match Sys.argv with
  | [|_|] -> parent ()
  | [|_; "child"|] -> child ()
  | [|_; "subchild"|] -> sub_child ()
  | _ -> invalid_arg "Sys.argv"

I think that makes a compelling case for the idea @alainfrisch
described:

A related remark only : I think it would seem coherent with the
treatment of file descriptors in the Unix library to represent
process descriptors with a dedicated abstract type which would be
implemented as the integer representing the pid on Unix, and as the
handle on Windows.

which entails adding a new type, new functions to win32unix and a
wrapper for OpenProcess.

Note also that Linux 5.13 has introduced pidfd which share
similarities to Windows process handles.

I hope I got this right! Thanks.

cc @dra27 @nojb

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions