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?