Race condition results in Assembly reference equality being false #70742
Description
Description
I was investigating a random instability running our NUnit tests. The issue traced back to two assembly instances not being equal when I'm pretty sure they should be.
There is a race condition that results in the following randomly being false when multiple threads trigger the loading of an assembly for the first time
static void Worker()
{
var asm1 = typeof(Class1).Assembly;
var asm2 = typeof(Class1).Assembly;
if (asm1 != asm2)
Console.WriteLine("Assemblies were not equal!!!");
}
When this happens, one of the assembly instances will not appear in AssemblyLoadContext.Default.Assemblies
I was able to reproduce this on both Windows & Mac using .NET 6.0.100.
I was able to reproduce using .NET 5.0.301 on windows. I didn't test Mac. The issue appears to be much more common on net5. Every time I've ran on net5 the issue has happened in less than 10 iterations.
Reproduction Steps
-
Check out the repro project here https://github.com/mrvoorhe/AssemblyRaceBugRepro
-
dotnet build
-
dotnet run --project Runner/Runner.csproj --framework net6.0
-
Wait for the program to end.
If the issue doesn't happen, there are some parameters you can tweak at the top of Runner/Program.cs
that may make it easier to reproduce
Expected behavior
Quiting. The issue never happened
Actual behavior
Assemblies were not equal!!!
asm1.GetHashCode() = 4032828
asm2.GetHashCode() = 37489757
Default ALC contains asm1 = False
Default ALC contains asm2 = True
Run 76 complete
Quiting because behavior was reproduced
Regression?
The issue does not seem to happen on .NET Framework. I've completed a few thousand iterations without hitting the problem.
I was able to reproduce the issue using .NET 5.0.301 on windows. I didn't test net5 on mac. The issue appears to be much more common on net5. Every time I've ran on net5 the issue has happened in less than 10 iterations.
The repro project has targets setup for net5.0 and net48
Known Workarounds
I used
public static bool EqualsWorkaroundForInstability(this Assembly assembly, Assembly other)
{
return assembly.FullName == other.FullName && assembly.Location == other.Location;
}
To workaround the issue
Configuration
I reproduced on 6.0.100 and 5.0.301
I reproduced on Windows 11 and macOS Monterey
x64 Windows. M1 Mac Pro.
Other information
No response