This question is an extension of the answer to another question about memory barriers:
https://stackoverflow.com/a/3556877/13085654
Say you take that code example and tweak it to make the method async:
class Program
{
static bool stop = false;
public static void Main(string[] args)
{
var t = new Thread(async () =>
{
Console.WriteLine("thread begin");
while (!stop)
{
if (false)
{
await default(Task);
}
}
Console.WriteLine("thread end");
});
t.Start();
Thread.Sleep(1000);
stop = true;
Console.WriteLine("stop = true");
Console.WriteLine("waiting...");
t.Join();
}
}
Obviously the await will never be hit, but just its presence in the method somehow allows the Release-compiled program to finish: Commenting out await default(Task); causes the program to once again hang (even if you leave the method marked async). This made me think that the compiler-generated state machine is significantly different based on whether or not the method contains at least one await. But comparing the IL for the parts of the method that are actually hit shows nearly identical instructions, both containing the following loop construct:
// start of loop, entry point: IL_0039
// [14 13 - 14 26]
IL_0039: ldsfld bool Program::stop
IL_003e: brfalse.s IL_0039
// end of loop
At the IL level, I don't see what the C# compiler is doing to inject a memory barrier when an await is present. Does anyone have any insight as to how this is happening?