Do I always have to await on an Async method of a disposable object instead of returning its Task?

Viewed 107

Seems I fell into a massive gotcha and after hours of debugging I saw that process goes out of "using" scope in RunGame1() and the Task never completes when awaited upon.

async void Execute(object o)  // ICommand.Execute
{
    // fire and forget
    try
    {
        await RunGame2();  // RunGame1() fails to complete
    }
    finally
    { ... }
}

// This fails due to process going out of "using" scope and never completes
Task RunGame1()
{
    var info = new ProcessStartInfo("game.exe") { CreateNoWindow = true };
    using var process = new Process() { StartInfo = info };
    process.Start();
    return process.WaitForExitAsync();
}

// Have to await the Task inside the method
async Task RunGame2()
{
    var info = new ProcessStartInfo("game.exe") { CreateNoWindow = true };
    using var process = new Process() { StartInfo = info };
    process.Start();
    await process.WaitForExitAsync();
}

Is there a pattern to get around this so I can return the Task to save the compiler making an extra state machine or is it just something to be aware of?

1 Answers

This is a common gotcha indeed - I've seen several bugs due to similar.

In short:

  • no, you're not always required to await a task, and sometimes it can be actively advantageous to avoid the overhead of the async machinery - especially in tight code like file/network IO loop, but (and it is a big but)
  • if that means you're escaping a try/finally region (which also includes using and lock, although there are other bigger problems with lock in async code), then you need to take that into consideration, which usually means "yes, you need to await"

It would be nice if there was an analyzer that spotted the return of a Task[<T>]/ValueTask[<T>] in a non-async method, inside such a region, as it is almost always a bug, and for the few times that it isn't (where the finally has nothing to do with the thing being returned) it could be suppressed.

Related