Repro
using System;
using System.Runtime.CompilerServices;
using System.Threading;
internal sealed class Box
{
public int Value;
public int Ready;
}
internal static class Program
{
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
private static int Reader(Box box)
{
int value = 0;
int seen = 0;
while (seen < 2)
{
value = box.Value;
if (Volatile.Read(ref box.Ready) != 0)
{
seen++;
}
}
return value;
}
private static void Main()
{
var box = new Box();
var reader = new Thread(() => Console.WriteLine($"reader saw {Reader(box)}"));
reader.Start();
Thread.Sleep(50);
box.Value = 42;
Volatile.Write(ref box.Ready, 1);
reader.Join();
}
}
Run with DOTNET_TieredCompilation=0.
Expected
The loop exits only after two iterations saw Ready != 0, so the returned box.Value load runs
after an acquire in an earlier iteration. DOTNET_JitDoLoopHoisting=0 and DOTNET_JITMinOpts=1
both print this, 5 of 5 runs each.
Actual
Reproduced on 5 of 5 runs. The plain box.Value load is hoisted out of the loop even though it is
followed by a Volatile.Read acquire in the same loop body, so it is executed once before the
writer publishes and never re-read.
Platform
Windows x64, .NET 11 (dotnet/runtime main @ a0b86b1, Checked build). Requires
DOTNET_TieredCompilation=0; no special CPU features.
Repro
Run with
DOTNET_TieredCompilation=0.Expected
The loop exits only after two iterations saw
Ready != 0, so the returnedbox.Valueload runsafter an acquire in an earlier iteration.
DOTNET_JitDoLoopHoisting=0andDOTNET_JITMinOpts=1both print this, 5 of 5 runs each.
Actual
Reproduced on 5 of 5 runs. The plain
box.Valueload is hoisted out of the loop even though it isfollowed by a
Volatile.Readacquire in the same loop body, so it is executed once before thewriter publishes and never re-read.
Platform
Windows x64, .NET 11 (dotnet/runtime main @ a0b86b1, Checked build). Requires
DOTNET_TieredCompilation=0; no special CPU features.