2
votes

The following reduced example generates this compiler warning:

warning CS4014: Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.

public class TaskInterlockedExchangeTest
{
    private Task _Task;

    public async Task DoSomething()
    {
        Interlocked.Exchange(ref _Task, null);
        await _Task;
    }
}

The warning is raised on the line with the Interlocked.Exchange call. As far as I'm aware Interlocked.Exchange is not async so why on earth is the compiler triggering this warning? Right now this doesn't make any sense to me, so what am I missing?

We do treat warnings as errors so I'm trying to figure out how to fix this (short of disabling the warning around the offending code which I would only see as last resort).

This happens on VS2013 (update 5).

Update

I just discovered that the following avoids the warning:

public class TaskInterlockedExchangeTest
{
    private Task _Task;

    public async Task DoSomething()
    {
        var t = Interlocked.Exchange(ref _Task, null);
        await _Task;
    }
}

So simply assigning the result to a local variable is sufficient.

1
Exchange returns Task, try to call another method that returns Task from async method maybe the same warning will appear.csharpfolk
You're ignoring the return value of Interlocked, which is a Task. Stop ignoring it, and the warning will go away :)Luaan
Also, you're (most probably) awaiting on null... You probably meant var task = Interlocked.Exchange(ref _Task, null); if (task != null) await task;Lucas Trzesniewski
@Luaan reminder to self: always be pedantic on SO :-) I "fixed" my comment above ;)Lucas Trzesniewski
@LucasTrzesniewski: This was just a minimal example to reproduce the warning and not the actual production code.ChrisWue

1 Answers

6
votes

You're calling the generic overload of Interlocked.Exchange:

public static T Exchange<T>(ref T location1, T value)
    where T : class

Which means the result of the call is a Task. The warning works as designed. It sees you're calling a function which returns a Task, so is potentially asynchronous, but you don't await its result.

The line:

Interlocked.Exchange(ref _Task, null);

Means the following:

  • Replace whatever was in _Task with null
  • Throw out the previous value (given to you as the return value of Interlocked.Exchange)

The following line:

await _Task;

most probably is the equivalent of:

await null;

And will go boom.

I say most probably, since there's a tiny possibility another thread wrote something into _Task, and you're going to await that.

I guess the real code you wanted to write is the following:

public async Task DoSomething()
{
    var task = Interlocked.Exchange(ref _Task, null);

    if (task != null)
        await task;
}

Or, with .NET 4.6:

public async Task DoSomething()
    => await (Interlocked.Exchange(ref _Task, null) ?? Task.CompletedTask);

You may even get rid of the async:

public Task DoSomething()
    => Interlocked.Exchange(ref _Task, null) ?? Task.CompletedTask;