Process.WaitForExitAsync is not the async equivalent of Process.WaitForExit in .NET 5

  • Gérald Barré

.NET 5 introduces a new very handy method: Process.WaitForExitAsync(CancellationToken) (documentation). This method waits for the process to exit, or for the cancellationToken to be canceled. It is very similar to the synchronous version Process.WaitForExit and I'm pretty sure some analyzers will suggest replacing the method with the new asynchronous method.

Both methods behave differently. WaitForExit ensures that all processing has been completed, including the handling of asynchronous events for redirected standard output. WaitForExitAsync doesn't wait for redirected output to complete. I reported this issue too lately, so it won't be part of .NET 5 😦

The workaround is to call WaitForExit after WaitForExitAsync. This won't be fully asynchronous as waiting for the output will be synchronous, but it will be correct. To demonstrate this behavior, you can run this unit test:

[Fact]
public async Task Repro_WaitForExitAsync()
{
    var logs = new List<string>();
    var psi = new ProcessStartInfo("cmd", "/C echo test")
    {
        UseShellExecute = false,
        RedirectStandardOutput = true,
    };
    using var process = new Process();
    process.StartInfo = psi;
    process.OutputDataReceived += (sender, e) => { if (e.Data != null) logs.Add(e.Data); };
    process.Start();

    // Give time for the process (cmd) to terminate
    await Task.Delay(1000);

    process.BeginOutputReadLine();

    await process.WaitForExitAsync();
    Assert.Empty(logs); // ⚠ The collection is empty, but it should contain 1 item

    process.WaitForExit();
    Assert.Equal(new[] { "test" }, logs); // ok because WaitForExit waits for redirected streams
}

If you update your code to use asynchronous methods and you redirect the output stream, be careful with this method. You may introduce a very subtle bug.

Do you have a question or a suggestion about this post? Contact me!

Follow me:
Enjoy this blog?Buy Me A Coffee