2

I couldn't find a clear answer about whether returning from an async method always produces release semantics and whether await always produces acquire semantics. I assume yes, because otherwise any async/await code would be a minefield?

So here's an example: are returned values both guaranteed to be 100*2 and 12345*2, without any explicit locks or barriers?

private static async Task<(int, int)> AMethod()
{
    // Runs on the original thread:
    var x = 100;
    var y = 12345;

    var task = Task.Run(() =>
    {
        // Can run on another thread:
        x *= 2;
        y *= 2;

        // Implicit return here, marking the task completed.
        // Release semantics or not?
    });

    await task; // Acquire semantics or not?

    // Runs on the original thread:
    return (x, y);
}

EDIT: Of course, Task.Run also needs to produce a release and an acquire is needed when starting to run the task's code. Forgot about those in the original question.

relatively_random
  • 4,505
  • 1
  • 26
  • 48

1 Answers1

6

Yes, the returned values are both guaranteed to be 100*2 and 12345*2, without any explicit locks or barriers.

It's the Task.Run, not the await, which produces the memory barrier in this case.

To quote the wonderful Albahari Threading in C#:

The following implicitly generate full fences:

  • C#'s lock statement (Monitor.Enter/Monitor.Exit)
  • All methods on the Interlocked class (we’ll cover these soon)
  • Asynchronous callbacks that use the thread pool — these include asynchronous delegates, APM callbacks, and Task continuations
  • Setting and waiting on a signaling construct
  • Anything that relies on signaling, such as starting or waiting on a Task

By virtue of that last point, the following is thread-safe:

int x = 0;
Task t = Task.Factory.StartNew (() => x++);
t.Wait();
Console.WriteLine (x);    // 1

Task.Run wraps ThreadPool.UnsafeQueueUserWorkItem, which falls under "Asynchronous callbacks that use the thread pool".

See Memory barrier generators for a more comprensive list of things that create memory barriers.

Community
  • 1
  • 1
canton7
  • 37,633
  • 3
  • 64
  • 77
  • Of course, acquire and release are required for starting the task as well, not just finishing. Duh. Thanks for mentioning that. Will edit the question. – relatively_random Mar 13 '19 at 10:16