1

Calling process.Dispose() or process.Close() (no matter which one, 'cos .Close is called inside .Dispose implementation) after process.Kill() sometimes hangs application.

I can't reproduce this bug stable, but sometimes when WaitForExit finishes by timeout passed to it, application hangs on process.Close() command.

Please suggest me, what the cause of this problem may be?

Note:

  • I've seen similar question. But there are not answers & upvoted comment says, that the cause of the problem perhaps at details, which are not provided at that question. So I added more details.
  • I've also seen a linked solution, but I can't use ProcessStartInfo.UseShellExecute = true; 'cos I need to redirect output.

Sorry for verbose code, but I pass it such way, 'cos at similar unanswered question commentators noticed, that not enough details provided (as I noted above)

private static async Task<int> RunMethod(string processArguments)
{
    // 1. Prepare ProcessStartInfo

    ProcessStartInfo startInfo = new ProcessStartInfo();

    startInfo.Arguments = processArguments;
    startInfo.RedirectStandardOutput = true;
    startInfo.RedirectStandardError = true;
    startInfo.CreateNoWindow = true;
    startInfo.UseShellExecute = false;

    // 2. Create process inside using-block to be disposed

    using (var proc = new Process())
    {
        proc.StartInfo = startInfo;

        // 3. Subscribe output streams handlers

        proc.OutputDataReceived += (sender, outputLine) => { HandleMessage(outputLine); };
        proc.ErrorDataReceived  += (sender, errorLine)  => { HandleMessage(errorLine); };

        // 4. Start process

        if (!proc.Start())
        {
            proc.Close();
            return -1;
        }

        // 5. Start the asynchronous read of the standard output stream.

        proc.BeginOutputReadLine();
        proc.BeginErrorReadLine();

        // 6. Waiting process to exit with timeout on threadpool to not block UI thread

        // Re#er warns me "captured variable `proc` is disposed in the outer scope". But I think it's Ok, 'cos we're awaiting this task inside using block (in next line)
        var waitingProcessTask = Task.Run(() => proc.WaitForExit(TIMEOUT), _cancelToken);
        bool hasExited = await waitingProcessTask;

        // 7. Stop reading streams

        // Not sure, these 2 CalncelXxxRead methods are needed. But hope it won't hurt at least
        proc.CancelErrorRead();
        proc.CancelOutputRead();

        // 8. If !hasExited (i.e. TIMEOUT is reached) we kill the process

        if (!hasExited)
        {
            Logger.Debug("0. Before Kill()");

            proc.Kill();
            proc.Refresh(); // not sure, it's needed
        }

        // If uncomment next 2 lines, then problem moves here from end of using block
        //proc.Close();
        //Logger.Debug("1. after .Close call");  // <------------------ This log we don't see sometimes
        
        Logger.Debug("2. last inside using-block");
    } // end of using block

    Logger.Debug("3. after using-block");  // <------------------ This log we don't see sometimes (if `.Close` wasn't called before)
    
    return 0;
}
user1234567
  • 3,991
  • 3
  • 19
  • 25
  • 4
    It's not disposing per se that's the problem, it's the async stream read line functionality that causes race conditions. Read the documentation on `Process`, it has numerous warnings about this -- if you use them it's your responsibility to write correct code or freeze your thread. – Blindy Jan 12 '21 at 18:12

1 Answers1

0

I've figured out with my issue. The problem was: my process once in a while** spawned child daemon-process, which is ment to work forever. Child process (by design) always inherits redirected output streams if parent streams are redirected.

From the other side, .Kill() don't kill child processes - only for which it was kalled. (Until .Net 5.0, where .Kill(bool) solves this problem).

So .Dispose() or .Close() never finish, 'cos waits to release output streams, which are held by infinite child process.

It was very interesting adventure to figure out, what is happening =)


**) - that's why reproduce was very unstable

PS: Thank to @Blindy for directing me the way quite close to where the real cause of problem lie.

user1234567
  • 3,991
  • 3
  • 19
  • 25