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

 
 
  • Gérald Barré

.NET 5 introduces a handy method: Process.WaitForExitAsync(CancellationToken) (documentation). This method waits for the process to exit or for the cancellation token to be canceled. It looks very similar to the synchronous Process.WaitForExit, and some analyzers will likely suggest replacing the synchronous call with this new asynchronous version.

However, the two methods behave differently. WaitForExit ensures that all processing is complete, including the handling of asynchronous events for redirected standard output. WaitForExitAsync does not wait for redirected output to complete. I reported this issue too late, so it won't be part of .NET 5 😦

The workaround is to call WaitForExit after WaitForExitAsync. This is not fully asynchronous since waiting for output remains synchronous, but it produces correct results. The following unit test demonstrates this behavior:

C#
[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 redirect the output stream, be careful with this method. It can introduce a very subtle bug.

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

Follow me:
Enjoy this blog?