Skip to content

Commit 5fc4a5f

Browse files
authored
Do not use collectible AssemblyLoadContexts in AnalyzerAssemblyLoader. (#79990)
We ship several analyzers and source-generators in the SDK that are added to most projects. We want to ship these as Ready2Run so that we reduce JIT time. However, when an assembly is loaded into a collectible AssemblyLoadContext it prevents any of the R2R logic from being used.
2 parents 4610f5a + ab4d18c commit 5fc4a5f

File tree

2 files changed

+28
-24
lines changed

2 files changed

+28
-24
lines changed

src/Compilers/Core/CodeAnalysisTest/AnalyzerAssemblyLoaderTests.cs

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,7 @@
2323
using Basic.Reference.Assemblies;
2424
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
2525
using Microsoft.CodeAnalysis.Emit;
26-
using Microsoft.CodeAnalysis.UnitTests.Diagnostics;
2726
using System.Diagnostics;
28-
using System.ComponentModel;
2927

3028
#if NET
3129
using Roslyn.Test.Utilities.CoreClr;
@@ -1653,7 +1651,7 @@ public void AssemblyLoadingInNonDefaultContext_AnalyzerReferencesSystemCollectio
16531651
// Load the compiler assembly and a modified version of S.C.I into the compiler load context. We
16541652
// expect the analyzer will use the bogus S.C.I in the compiler context instead of the one
16551653
// in the host context.
1656-
var alc = new AssemblyLoadContext(nameof(AssemblyResolver_FirstOneWins), isCollectible: true);
1654+
var alc = new AssemblyLoadContext(nameof(AssemblyResolver_FirstOneWins), isCollectible: false);
16571655
_ = alc.LoadFromAssemblyPath(TestFixture.UserSystemCollectionsImmutable);
16581656
_ = alc.LoadFromAssemblyPath(typeof(AnalyzerAssemblyLoader).GetTypeInfo().Assembly.Location);
16591657
var loader = kind switch
@@ -1677,8 +1675,33 @@ public void AssemblyLoadingInNonDefaultContext_AnalyzerReferencesSystemCollectio
16771675

16781676
Assert.Equal("42", sb.ToString());
16791677
});
1678+
}
16801679

1681-
alc.Unload();
1680+
[Theory]
1681+
[CombinatorialData]
1682+
public void AssemblyLoading_DoesNotUseCollectibleALCs(AnalyzerTestKind kind)
1683+
{
1684+
// This validation is critical to our VS / CLI performance. We ship several analyzers and source-generators in the
1685+
// SDK (NetAnalyzers & Razor generators) that are added to most projects. We want to ship these as Ready2Run so that
1686+
// we reduce JIT time. However, when an assembly is loaded into a collectible AssemblyLoadContext it prevents any of
1687+
// the R2R logic from being used.
1688+
1689+
Run(kind, static (AnalyzerAssemblyLoader loader, AssemblyLoadTestFixture testFixture) =>
1690+
{
1691+
loader.AddDependencyLocation(testFixture.Delta1);
1692+
loader.AddDependencyLocation(testFixture.Gamma);
1693+
1694+
Assembly gamma = loader.LoadFromPath(testFixture.Gamma);
1695+
Assert.NotNull(gamma);
1696+
1697+
var contexts = loader.GetDirectoryLoadContextsSnapshot();
1698+
Assert.NotEmpty(contexts);
1699+
1700+
foreach (var context in contexts)
1701+
{
1702+
Assert.False(context.IsCollectible, "AnalyzerAssemblyLoader should not use collectible assembly load contexts.");
1703+
}
1704+
});
16821705
}
16831706
#endif
16841707

src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerAssemblyLoader.Core.cs

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -165,29 +165,10 @@ internal DirectoryLoadContext[] GetDirectoryLoadContextsSnapshot()
165165

166166
private partial void DisposeWorker()
167167
{
168-
var contexts = ArrayBuilder<DirectoryLoadContext>.GetInstance();
169168
lock (_guard)
170169
{
171-
foreach (var (_, context) in _loadContextByDirectory)
172-
contexts.Add(context);
173-
174170
_loadContextByDirectory.Clear();
175171
}
176-
177-
foreach (var context in contexts)
178-
{
179-
try
180-
{
181-
context.Unload();
182-
CodeAnalysisEventSource.Log.DisposeAssemblyLoadContext(context.Directory, context.ToString());
183-
}
184-
catch (Exception ex) when (FatalError.ReportAndCatch(ex, ErrorSeverity.Critical))
185-
{
186-
CodeAnalysisEventSource.Log.DisposeAssemblyLoadContextException(context.Directory, ex.ToString(), context.ToString());
187-
}
188-
}
189-
190-
contexts.Free();
191172
}
192173

193174
internal sealed class DirectoryLoadContext : AssemblyLoadContext
@@ -196,7 +177,7 @@ internal sealed class DirectoryLoadContext : AssemblyLoadContext
196177
private readonly AnalyzerAssemblyLoader _loader;
197178

198179
public DirectoryLoadContext(string directory, AnalyzerAssemblyLoader loader)
199-
: base(isCollectible: true)
180+
: base(isCollectible: false)
200181
{
201182
Directory = directory;
202183
_loader = loader;

0 commit comments

Comments
 (0)