1

The following code hangs in the call to WaitForExit at the end. It terminates cleanly if I remove the 1 second long sleep. Could someone please tell me what to do so the process would die shortly after I call Kill() on it? Many thanks.

    let processStartInfo = System.Diagnostics.ProcessStartInfo("c:/cygwin/bin/bash.exe", "-c yes")
    processStartInfo.CreateNoWindow <- true
    processStartInfo.UseShellExecute <- false
    processStartInfo.RedirectStandardOutput <- true
    processStartInfo.RedirectStandardInput <- false
    processStartInfo.RedirectStandardError <- true               

    use proc = new System.Diagnostics.Process ()                        
    proc.StartInfo <- processStartInfo

    let f _ = ()
    proc.OutputDataReceived.Add f
    proc.ErrorDataReceived.Add f         

    if not (proc.Start()) then 
        failwithf "Could not start"

    proc.BeginErrorReadLine ()
    proc.BeginOutputReadLine ()

    // the process terminates fine without this
    System.Threading.Thread.Sleep (1000) 

    printf "Waiting to die"

    proc.Kill ()              // this does not seem to work
    proc.CancelOutputRead ()
    proc.CancelErrorRead ()
    proc.WaitForExit()        // execution gets stuck here, apparently forever
Joe Huha
  • 548
  • 3
  • 16
  • 1
    Surely the process actually terminated, use Task Manager to verify. WaitForExit() does not just wait for the process to terminate, it also waits until all redirected output has been read. Why that did not happen is hard to guess, other than that Cygwin is very quirky. Moving the Kill() call past the cancel calls probably helps. – Hans Passant May 15 '15 at 01:28
  • I turns out you are right on the first part - many thanks. I think the problem is that WaitForExit will not terminate if the process is already dead (this directly contradicts M$ documentation that specifically says to call it after kill()). So the fix was to just provide a timeout and retry for as long as the process is alive. BTW, the order of the cancels does not seem to make a difference. – Joe Huha May 15 '15 at 07:41

1 Answers1

0

I don't know F#, but this code has a few very strange things; Process.Read() is a blocking call, so you either have to make them asynchronous or call .ReadToEnd() instead if you want to wait until the thing redirected to finishes. I would use proc.Dispose() or put it in a using block in C# instead of calling proc.kill(), which should be done after WaitForExit().

Check out my answer to Opening a DOS Console.

Also check out this reply saying that if you redirect both stdOut and stdErr, you must make at least one asynchronous.

Community
  • 1
  • 1
Dour High Arch
  • 21,513
  • 29
  • 75
  • 90
  • Many thanks, I don't think I am using Process.Read()? In any case, both stdout and stderr reads are asynchronous? Are you sure that kill() should be done after waitforexit and not the other way round? [NB: The use block will call Dispose once proc leaves the scope. I suspect I probably should not call Dispose while the process is still running?] – Joe Huha May 14 '15 at 22:20
  • I have accepted this answer as a courtesy. See my reply to H Passant's comment if you have the same problem. – Joe Huha May 25 '15 at 13:09
  • Thanks @Joe, but if you found an answer to your own question you should post an answer yourself. After a day or so you can accept your own answer. That will help other people. – Dour High Arch May 25 '15 at 15:44
  • It was really Hans who came up with the key insight, and I don't want to claim credit for that. I don't think it's specifically against the rules to accept an answer as way of thanking for the contributors time, even if in the ideal world it would be better to accept someone else's comment. Thanks again. – Joe Huha May 26 '15 at 12:47