提交 01df6918 编写于 作者: M Manish Vasani

[Performance] Make core AnalyzerExecutor async

This change replaces synchronous lock statements in the core analyzer executor and analyzer state tracker (for IDE live analysis) with semaphore slim DisposableWaitAsync invocations. This ensures that most of the analyzer driver and analyzer executor code is now async, with only the eventual callback into the analyzers being a synchronous method ([ExecuteAndCatchIfThrows_NoLock](http://source.roslyn.io/Microsoft.CodeAnalysis/R/02d7df8203d3591e.html))

Testing: I verified that the self-build time of Roslyn.sln (which uses a few analyzers), is almost identical after this change. We are hoping that this will improve build performance when there is lot of lock contention, which has shown up from perf traces (see https://github.com/dotnet/roslyn/issues/6053).

Fixes #6399
上级 80d3bb71
......@@ -184,6 +184,8 @@
<Compile Include="InternalUtilities\CompilerOptionParseUtilities.cs" />
<Compile Include="InternalUtilities\ImmutableArrayInterop.cs" />
<Compile Include="InternalUtilities\ReflectionUtilities.cs" />
<Compile Include="InternalUtilities\SemaphoreSlimExtensions.cs" />
<Compile Include="InternalUtilities\SemaphoreSlimFactory.cs" />
<Compile Include="CorLightup.cs" />
<Compile Include="Desktop\AssemblyPortabilityPolicy.cs" />
<Compile Include="Desktop\AssemblyVersion.cs" />
......
......@@ -4,6 +4,8 @@
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Diagnostics.Telemetry;
using Roslyn.Utilities;
......@@ -16,7 +18,7 @@ internal partial class AnalysisState
{
private class PerAnalyzerState
{
private readonly object _gate = new object();
private readonly SemaphoreSlim _gate = new SemaphoreSlim(initialCount: 1);
private readonly Dictionary<CompilationEvent, AnalyzerStateData> _pendingEvents = new Dictionary<CompilationEvent, AnalyzerStateData>();
private readonly Dictionary<ISymbol, AnalyzerStateData> _pendingSymbols = new Dictionary<ISymbol, AnalyzerStateData>();
private readonly Dictionary<SyntaxNode, DeclarationAnalyzerStateData> _pendingDeclarations = new Dictionary<SyntaxNode, DeclarationAnalyzerStateData>();
......@@ -33,35 +35,36 @@ public PerAnalyzerState(ObjectPool<AnalyzerStateData> analyzerStateDataPool, Obj
public IEnumerable<CompilationEvent> PendingEvents_NoLock => _pendingEvents.Keys;
public bool HasPendingSyntaxAnalysis(SyntaxTree treeOpt)
public async Task<bool> HasPendingSyntaxAnalysisAsync(SyntaxTree treeOpt, CancellationToken cancellationToken)
{
lock (_gate)
using (await _gate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false))
{
return _lazyPendingSyntaxAnalysisTrees != null &&
(treeOpt != null ? _lazyPendingSyntaxAnalysisTrees.ContainsKey(treeOpt) : _lazyPendingSyntaxAnalysisTrees.Count > 0);
}
}
public bool HasPendingSymbolAnalysis(ISymbol symbol)
public async Task<bool> HasPendingSymbolAnalysisAsync(ISymbol symbol, CancellationToken cancellationToken)
{
lock (_gate)
using (await _gate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false))
{
return _pendingSymbols.ContainsKey(symbol);
}
}
private bool TryStartProcessingEntity<TAnalysisEntity, TAnalyzerStateData>(TAnalysisEntity analysisEntity, Dictionary<TAnalysisEntity, TAnalyzerStateData> pendingEntities, ObjectPool<TAnalyzerStateData> pool, out TAnalyzerStateData newState)
private async Task<TAnalyzerStateData> TryStartProcessingEntityAsync<TAnalysisEntity, TAnalyzerStateData>(TAnalysisEntity analysisEntity, Dictionary<TAnalysisEntity, TAnalyzerStateData> pendingEntities, ObjectPool<TAnalyzerStateData> pool, CancellationToken cancellationToken)
where TAnalyzerStateData : AnalyzerStateData, new()
{
lock (_gate)
using (await _gate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false))
{
return TryStartProcessingEntity_NoLock(analysisEntity, pendingEntities, pool, out newState);
return TryStartProcessingEntity_NoLock(analysisEntity, pendingEntities, pool);
}
}
private static bool TryStartProcessingEntity_NoLock<TAnalysisEntity, TAnalyzerStateData>(TAnalysisEntity analysisEntity, Dictionary<TAnalysisEntity, TAnalyzerStateData> pendingEntities, ObjectPool<TAnalyzerStateData> pool, out TAnalyzerStateData state)
private static TAnalyzerStateData TryStartProcessingEntity_NoLock<TAnalysisEntity, TAnalyzerStateData>(TAnalysisEntity analysisEntity, Dictionary<TAnalysisEntity, TAnalyzerStateData> pendingEntities, ObjectPool<TAnalyzerStateData> pool)
where TAnalyzerStateData : AnalyzerStateData
{
TAnalyzerStateData state;
if (pendingEntities.TryGetValue(analysisEntity, out state) &&
(state == null || state.StateKind == StateKind.ReadyToProcess))
{
......@@ -73,17 +76,16 @@ public bool HasPendingSymbolAnalysis(ISymbol symbol)
state.SetStateKind(StateKind.InProcess);
Debug.Assert(state.StateKind == StateKind.InProcess);
pendingEntities[analysisEntity] = state;
return true;
return state;
}
state = null;
return false;
return null;
}
private void MarkEntityProcessed<TAnalysisEntity, TAnalyzerStateData>(TAnalysisEntity analysisEntity, Dictionary<TAnalysisEntity, TAnalyzerStateData> pendingEntities, ObjectPool<TAnalyzerStateData> pool)
private async Task MarkEntityProcessedAsync<TAnalysisEntity, TAnalyzerStateData>(TAnalysisEntity analysisEntity, Dictionary<TAnalysisEntity, TAnalyzerStateData> pendingEntities, ObjectPool<TAnalyzerStateData> pool, CancellationToken cancellationToken)
where TAnalyzerStateData : AnalyzerStateData
{
lock (_gate)
using (await _gate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false))
{
MarkEntityProcessed_NoLock(analysisEntity, pendingEntities, pool);
}
......@@ -104,10 +106,10 @@ public bool HasPendingSymbolAnalysis(ISymbol symbol)
}
}
private bool IsEntityFullyProcessed<TAnalysisEntity, TAnalyzerStateData>(TAnalysisEntity analysisEntity, Dictionary<TAnalysisEntity, TAnalyzerStateData> pendingEntities)
private async Task<bool> IsEntityFullyProcessedAsync<TAnalysisEntity, TAnalyzerStateData>(TAnalysisEntity analysisEntity, Dictionary<TAnalysisEntity, TAnalyzerStateData> pendingEntities, CancellationToken cancellationToken)
where TAnalyzerStateData : AnalyzerStateData
{
lock (_gate)
using (await _gate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false))
{
return IsEntityFullyProcessed_NoLock(analysisEntity, pendingEntities);
}
......@@ -119,58 +121,58 @@ public bool HasPendingSymbolAnalysis(ISymbol symbol)
return !pendingEntities.ContainsKey(analysisEntity);
}
public bool TryStartProcessingEvent(CompilationEvent compilationEvent, out AnalyzerStateData state)
public async Task<AnalyzerStateData> TryStartProcessingEventAsync(CompilationEvent compilationEvent, CancellationToken cancellationToken)
{
return TryStartProcessingEntity(compilationEvent, _pendingEvents, _analyzerStateDataPool, out state);
return await TryStartProcessingEntityAsync(compilationEvent, _pendingEvents, _analyzerStateDataPool, cancellationToken).ConfigureAwait(false);
}
public void MarkEventComplete(CompilationEvent compilationEvent)
public async Task MarkEventCompleteAsync(CompilationEvent compilationEvent, CancellationToken cancellationToken)
{
MarkEntityProcessed(compilationEvent, _pendingEvents, _analyzerStateDataPool);
await MarkEntityProcessedAsync(compilationEvent, _pendingEvents, _analyzerStateDataPool, cancellationToken).ConfigureAwait(false);
}
public bool TryStartAnalyzingSymbol(ISymbol symbol, out AnalyzerStateData state)
public async Task<AnalyzerStateData> TryStartAnalyzingSymbolAsync(ISymbol symbol, CancellationToken cancellationToken)
{
return TryStartProcessingEntity(symbol, _pendingSymbols, _analyzerStateDataPool, out state);
return await TryStartProcessingEntityAsync(symbol, _pendingSymbols, _analyzerStateDataPool, cancellationToken).ConfigureAwait(false);
}
public void MarkSymbolComplete(ISymbol symbol)
public async Task MarkSymbolCompleteAsync(ISymbol symbol, CancellationToken cancellationToken)
{
MarkEntityProcessed(symbol, _pendingSymbols, _analyzerStateDataPool);
await MarkEntityProcessedAsync(symbol, _pendingSymbols, _analyzerStateDataPool, cancellationToken).ConfigureAwait(false);
}
public bool TryStartAnalyzingDeclaration(SyntaxReference decl, out DeclarationAnalyzerStateData state)
public async Task<DeclarationAnalyzerStateData> TryStartAnalyzingDeclarationAsync(SyntaxReference decl, CancellationToken cancellationToken)
{
return TryStartProcessingEntity(decl.GetSyntax(), _pendingDeclarations, _declarationAnalyzerStateDataPool, out state);
return await TryStartProcessingEntityAsync(decl.GetSyntax(), _pendingDeclarations, _declarationAnalyzerStateDataPool, cancellationToken).ConfigureAwait(false);
}
public bool IsDeclarationComplete(SyntaxNode decl)
public async Task<bool> IsDeclarationCompleteAsync(SyntaxNode decl, CancellationToken cancellationToken)
{
return IsEntityFullyProcessed(decl, _pendingDeclarations);
return await IsEntityFullyProcessedAsync(decl, _pendingDeclarations, cancellationToken).ConfigureAwait(false);
}
public void MarkDeclarationComplete(SyntaxReference decl)
public async Task MarkDeclarationCompleteAsync(SyntaxReference decl, CancellationToken cancellationToken)
{
MarkEntityProcessed(decl.GetSyntax(), _pendingDeclarations, _declarationAnalyzerStateDataPool);
await MarkEntityProcessedAsync(decl.GetSyntax(), _pendingDeclarations, _declarationAnalyzerStateDataPool, cancellationToken).ConfigureAwait(false);
}
public bool TryStartSyntaxAnalysis(SyntaxTree tree, out AnalyzerStateData state)
public async Task<AnalyzerStateData> TryStartSyntaxAnalysisAsync(SyntaxTree tree, CancellationToken cancellationToken)
{
Debug.Assert(_lazyPendingSyntaxAnalysisTrees != null);
return TryStartProcessingEntity(tree, _lazyPendingSyntaxAnalysisTrees, _analyzerStateDataPool, out state);
return await TryStartProcessingEntityAsync(tree, _lazyPendingSyntaxAnalysisTrees, _analyzerStateDataPool, cancellationToken).ConfigureAwait(false);
}
public void MarkSyntaxAnalysisComplete(SyntaxTree tree)
public async Task MarkSyntaxAnalysisCompleteAsync(SyntaxTree tree, CancellationToken cancellationToken)
{
if (_lazyPendingSyntaxAnalysisTrees != null)
{
MarkEntityProcessed(tree, _lazyPendingSyntaxAnalysisTrees, _analyzerStateDataPool);
await MarkEntityProcessedAsync(tree, _lazyPendingSyntaxAnalysisTrees, _analyzerStateDataPool, cancellationToken).ConfigureAwait(false);
}
}
public void MarkDeclarationsComplete(ImmutableArray<SyntaxReference> declarations)
public async Task MarkDeclarationsCompleteAsync(ImmutableArray<SyntaxReference> declarations, CancellationToken cancellationToken)
{
lock (_gate)
using (await _gate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false))
{
foreach (var syntaxRef in declarations)
{
......@@ -179,9 +181,9 @@ public void MarkDeclarationsComplete(ImmutableArray<SyntaxReference> declaration
}
}
public void OnCompilationEventGenerated(CompilationEvent compilationEvent, AnalyzerActionCounts actionCounts)
public async Task OnCompilationEventGeneratedAsync(CompilationEvent compilationEvent, AnalyzerActionCounts actionCounts, CancellationToken cancellationToken)
{
lock (_gate)
using (await _gate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false))
{
var symbolEvent = compilationEvent as SymbolDeclaredCompilationEvent;
if (symbolEvent != null)
......@@ -235,14 +237,14 @@ public void OnCompilationEventGenerated(CompilationEvent compilationEvent, Analy
}
}
public bool IsEventAnalyzed(CompilationEvent compilationEvent)
public async Task<bool> IsEventAnalyzedAsync(CompilationEvent compilationEvent, CancellationToken cancellationToken)
{
return IsEntityFullyProcessed(compilationEvent, _pendingEvents);
return await IsEntityFullyProcessedAsync(compilationEvent, _pendingEvents, cancellationToken).ConfigureAwait(false);
}
public void OnSymbolDeclaredEventProcessed(SymbolDeclaredCompilationEvent symbolDeclaredEvent)
public async Task OnSymbolDeclaredEventProcessedAsync(SymbolDeclaredCompilationEvent symbolDeclaredEvent, CancellationToken cancellationToken)
{
lock (_gate)
using (await _gate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false))
{
OnSymbolDeclaredEventProcessed_NoLock(symbolDeclaredEvent);
}
......
......@@ -18,7 +18,7 @@ namespace Microsoft.CodeAnalysis.Diagnostics
/// </summary>
internal partial class AnalysisState
{
private readonly object _gate;
private readonly SemaphoreSlim _gate;
/// <summary>
/// Per-analyzer analysis state map.
......@@ -60,7 +60,7 @@ internal partial class AnalysisState
public AnalysisState(ImmutableArray<DiagnosticAnalyzer> analyzers)
{
_gate = new object();
_gate = new SemaphoreSlim(initialCount: 1);
_analyzerStateMap = CreateAnalyzerStateMap(analyzers, out _analyzerStates);
_pendingSourceEvents = new Dictionary<SyntaxTree, HashSet<CompilationEvent>>();
_pendingNonSourceEvents = new HashSet<CompilationEvent>();
......@@ -100,13 +100,13 @@ public async Task OnCompilationEventsGeneratedAsync(ImmutableArray<CompilationEv
{
await EnsureAnalyzerActionCountsInitializedAsync(driver, cancellationToken).ConfigureAwait(false);
lock (_gate)
using (await _gate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false))
{
OnCompilationEventsGenerated_NoLock(compilationEvents, driver, cancellationToken);
await OnCompilationEventsGenerated_NoLockAsync(compilationEvents, driver, cancellationToken).ConfigureAwait(false);
}
}
private void OnCompilationEventsGenerated_NoLock(ImmutableArray<CompilationEvent> compilationEvents, AnalyzerDriver driver, CancellationToken cancellationToken)
private async Task OnCompilationEventsGenerated_NoLockAsync(ImmutableArray<CompilationEvent> compilationEvents, AnalyzerDriver driver, CancellationToken cancellationToken)
{
Debug.Assert(_lazyAnalyzerActionCountsMap != null);
......@@ -126,7 +126,7 @@ private void OnCompilationEventsGenerated_NoLock(ImmutableArray<CompilationEvent
if (HasActionsForEvent(compilationEvent, actionCounts))
{
_pooledEventsWithAnyActionsSet.Add(compilationEvent);
analyzerState.OnCompilationEventGenerated(compilationEvent, actionCounts);
await analyzerState.OnCompilationEventGeneratedAsync(compilationEvent, actionCounts, cancellationToken).ConfigureAwait(false);
}
}
}
......@@ -284,12 +284,12 @@ private static bool HasActionsForEvent(CompilationEvent compilationEvent, Analyz
}
}
private void OnSymbolDeclaredEventProcessed(SymbolDeclaredCompilationEvent symbolDeclaredEvent, ImmutableArray<DiagnosticAnalyzer> analyzers)
private async Task OnSymbolDeclaredEventProcessedAsync(SymbolDeclaredCompilationEvent symbolDeclaredEvent, ImmutableArray<DiagnosticAnalyzer> analyzers, CancellationToken cancellationToken)
{
foreach (var analyzer in analyzers)
{
var analyzerState = GetAnalyzerState(analyzer);
analyzerState.OnSymbolDeclaredEventProcessed(symbolDeclaredEvent);
await analyzerState.OnSymbolDeclaredEventProcessedAsync(symbolDeclaredEvent, cancellationToken).ConfigureAwait(false);
}
}
......@@ -297,26 +297,27 @@ private void OnSymbolDeclaredEventProcessed(SymbolDeclaredCompilationEvent symbo
/// Invoke this method at completion of event processing for the given analysis scope.
/// It updates the analysis state of this event for each analyzer and if the event has been fully processed for all analyzers, then removes it from our event cache.
/// </summary>
public void OnCompilationEventProcessed(CompilationEvent compilationEvent, AnalysisScope analysisScope)
public async Task OnCompilationEventProcessedAsync(CompilationEvent compilationEvent, AnalysisScope analysisScope, CancellationToken cancellationToken)
{
// Analyze if the symbol and all its declaring syntax references are analyzed.
var symbolDeclaredEvent = compilationEvent as SymbolDeclaredCompilationEvent;
if (symbolDeclaredEvent != null)
{
OnSymbolDeclaredEventProcessed(symbolDeclaredEvent, analysisScope.Analyzers);
await OnSymbolDeclaredEventProcessedAsync(symbolDeclaredEvent, analysisScope.Analyzers, cancellationToken).ConfigureAwait(false);
}
// Check if event is fully analyzed for all analyzers.
foreach (var analyzerState in _analyzerStates)
{
if (!analyzerState.IsEventAnalyzed(compilationEvent))
var eventAnalyzed = await analyzerState.IsEventAnalyzedAsync(compilationEvent, cancellationToken).ConfigureAwait(false);
if (!eventAnalyzed)
{
return;
}
}
// Remove the event from event map.
lock (_gate)
using (await _gate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false))
{
UpdateEventsMap_NoLock(compilationEvent, add: false);
}
......@@ -330,9 +331,9 @@ public void OnCompilationEventProcessed(CompilationEvent compilationEvent, Analy
/// <summary>
/// Gets pending events for given set of analyzers for the given syntax tree.
/// </summary>
public ImmutableArray<CompilationEvent> GetPendingEvents(ImmutableArray<DiagnosticAnalyzer> analyzers, SyntaxTree tree)
public async Task<ImmutableArray<CompilationEvent>> GetPendingEventsAsync(ImmutableArray<DiagnosticAnalyzer> analyzers, SyntaxTree tree, CancellationToken cancellationToken)
{
lock (_gate)
using (await _gate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false))
{
return GetPendingEvents_NoLock(analyzers, tree);
}
......@@ -389,9 +390,10 @@ private ImmutableArray<CompilationEvent> GetPendingEvents_NoLock(ImmutableArray<
/// <param name="analyzers"></param>
/// <param name="includeSourceEvents">Indicates if source events (symbol declared, compilation unit completed event) should be included.</param>
/// <param name="includeNonSourceEvents">Indicates if compilation wide events (compilation started and completed event) should be included.</param>
public ImmutableArray<CompilationEvent> GetPendingEvents(ImmutableArray<DiagnosticAnalyzer> analyzers, bool includeSourceEvents, bool includeNonSourceEvents)
/// <param name="cancellationToken">Cancellation token.</param>
public async Task<ImmutableArray<CompilationEvent>> GetPendingEventsAsync(ImmutableArray<DiagnosticAnalyzer> analyzers, bool includeSourceEvents, bool includeNonSourceEvents, CancellationToken cancellationToken)
{
lock (_gate)
using (await _gate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false))
{
return GetPendingEvents_NoLock(analyzers, includeSourceEvents, includeNonSourceEvents);
}
......@@ -451,7 +453,7 @@ private void Free(HashSet<CompilationEvent> events)
/// <summary>
/// Returns true if we have any pending syntax analysis for given analysis scope.
/// </summary>
public bool HasPendingSyntaxAnalysis(AnalysisScope analysisScope)
public async Task<bool> HasPendingSyntaxAnalysisAsync(AnalysisScope analysisScope, CancellationToken cancellationToken)
{
if (analysisScope.IsTreeAnalysis && !analysisScope.IsSyntaxOnlyTreeAnalysis)
{
......@@ -461,7 +463,7 @@ public bool HasPendingSyntaxAnalysis(AnalysisScope analysisScope)
foreach (var analyzer in analysisScope.Analyzers)
{
var analyzerState = GetAnalyzerState(analyzer);
if (analyzerState.HasPendingSyntaxAnalysis(analysisScope.FilterTreeOpt))
if (await analyzerState.HasPendingSyntaxAnalysisAsync(analysisScope.FilterTreeOpt, cancellationToken).ConfigureAwait(false))
{
return true;
}
......@@ -473,11 +475,11 @@ public bool HasPendingSyntaxAnalysis(AnalysisScope analysisScope)
/// <summary>
/// Returns true if we have any pending symbol analysis for given analysis scope.
/// </summary>
public bool HasPendingSymbolAnalysis(AnalysisScope analysisScope)
public async Task<bool> HasPendingSymbolAnalysisAsync(AnalysisScope analysisScope, CancellationToken cancellationToken)
{
Debug.Assert(analysisScope.FilterTreeOpt != null);
var symbolDeclaredEvents = GetPendingSymbolDeclaredEvents(analysisScope.FilterTreeOpt);
var symbolDeclaredEvents = await GetPendingSymbolDeclaredEventsAsync(analysisScope.FilterTreeOpt, cancellationToken).ConfigureAwait(false);
foreach (var symbolDeclaredEvent in symbolDeclaredEvents)
{
if (analysisScope.ShouldAnalyze(symbolDeclaredEvent.Symbol))
......@@ -485,7 +487,7 @@ public bool HasPendingSymbolAnalysis(AnalysisScope analysisScope)
foreach (var analyzer in analysisScope.Analyzers)
{
var analyzerState = GetAnalyzerState(analyzer);
if (analyzerState.HasPendingSymbolAnalysis(symbolDeclaredEvent.Symbol))
if (await analyzerState.HasPendingSymbolAnalysisAsync(symbolDeclaredEvent.Symbol, cancellationToken).ConfigureAwait(false))
{
return true;
}
......@@ -496,11 +498,11 @@ public bool HasPendingSymbolAnalysis(AnalysisScope analysisScope)
return false;
}
private ImmutableArray<SymbolDeclaredCompilationEvent> GetPendingSymbolDeclaredEvents(SyntaxTree tree)
private async Task<ImmutableArray<SymbolDeclaredCompilationEvent>> GetPendingSymbolDeclaredEventsAsync(SyntaxTree tree, CancellationToken cancellationToken)
{
Debug.Assert(tree != null);
lock (_gate)
using (await _gate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false))
{
HashSet<CompilationEvent> compilationEvents;
if (!_pendingSourceEvents.TryGetValue(tree, out compilationEvents))
......@@ -516,62 +518,63 @@ private ImmutableArray<SymbolDeclaredCompilationEvent> GetPendingSymbolDeclaredE
/// Attempts to start processing a compilation event for the given analyzer.
/// </summary>
/// <returns>
/// Returns false if the event has already been processed for the analyzer OR is currently being processed by another task.
/// If true, then it returns a non-null <paramref name="state"/> representing partial analysis state for the given event for the given analyzer.
/// Returns null if the event has already been processed for the analyzer OR is currently being processed by another task.
/// Otherwise, returns a non-null state representing partial analysis state for the given event for the given analyzer.
/// </returns>
public bool TryStartProcessingEvent(CompilationEvent compilationEvent, DiagnosticAnalyzer analyzer, out AnalyzerStateData state)
public async Task<AnalyzerStateData> TryStartProcessingEventAsync(CompilationEvent compilationEvent, DiagnosticAnalyzer analyzer, CancellationToken cancellationToken)
{
return GetAnalyzerState(analyzer).TryStartProcessingEvent(compilationEvent, out state);
return await GetAnalyzerState(analyzer).TryStartProcessingEventAsync(compilationEvent, cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// Marks the given event as fully analyzed for the given analyzer.
/// </summary>
public void MarkEventComplete(CompilationEvent compilationEvent, DiagnosticAnalyzer analyzer)
public async Task MarkEventCompleteAsync(CompilationEvent compilationEvent, DiagnosticAnalyzer analyzer, CancellationToken cancellationToken)
{
GetAnalyzerState(analyzer).MarkEventComplete(compilationEvent);
await GetAnalyzerState(analyzer).MarkEventCompleteAsync(compilationEvent, cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// Attempts to start processing a symbol for the given analyzer's symbol actions.
/// </summary>
/// <returns>
/// Returns false if the symbol has already been processed for the analyzer OR is currently being processed by another task.
/// If true, then it returns a non-null <paramref name="state"/> representing partial analysis state for the given symbol for the given analyzer.
/// Returns null if the symbol has already been processed for the analyzer OR is currently being processed by another task.
/// Otherwise, returns a non-null state representing partial analysis state for the given symbol for the given analyzer.
/// </returns>
public bool TryStartAnalyzingSymbol(ISymbol symbol, DiagnosticAnalyzer analyzer, out AnalyzerStateData state)
public async Task<AnalyzerStateData> TryStartAnalyzingSymbolAsync(ISymbol symbol, DiagnosticAnalyzer analyzer, CancellationToken cancellationToken)
{
return GetAnalyzerState(analyzer).TryStartAnalyzingSymbol(symbol, out state);
return await GetAnalyzerState(analyzer).TryStartAnalyzingSymbolAsync(symbol, cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// Marks the given symbol as fully analyzed for the given analyzer.
/// </summary>
public void MarkSymbolComplete(ISymbol symbol, DiagnosticAnalyzer analyzer)
public async Task MarkSymbolCompleteAsync(ISymbol symbol, DiagnosticAnalyzer analyzer, CancellationToken cancellationToken)
{
GetAnalyzerState(analyzer).MarkSymbolComplete(symbol);
await GetAnalyzerState(analyzer).MarkSymbolCompleteAsync(symbol, cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// Attempts to start processing a symbol declaration for the given analyzer's syntax node and code block actions.
/// </summary>
/// <returns>
/// Returns false if the declaration has already been processed for the analyzer OR is currently being processed by another task.
/// If true, then it returns a non-null <paramref name="state"/> representing partial analysis state for the given declaration for the given analyzer.
/// Returns null if the declaration has already been processed for the analyzer OR is currently being processed by another task.
/// Otherwise, returns a non-null state representing partial analysis state for the given declaration for the given analyzer.
/// </returns>
public bool TryStartAnalyzingDeclaration(SyntaxReference decl, DiagnosticAnalyzer analyzer, out DeclarationAnalyzerStateData state)
public async Task<DeclarationAnalyzerStateData> TryStartAnalyzingDeclarationAsync(SyntaxReference decl, DiagnosticAnalyzer analyzer, CancellationToken cancellationToken)
{
return GetAnalyzerState(analyzer).TryStartAnalyzingDeclaration(decl, out state);
return await GetAnalyzerState(analyzer).TryStartAnalyzingDeclarationAsync(decl, cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// True if the given symbol declaration is fully analyzed.
/// Returns true if the given symbol declaration is fully analyzed.
/// </summary>
public bool IsDeclarationComplete(SyntaxNode decl)
public async Task<bool> IsDeclarationCompleteAsync(SyntaxNode decl, CancellationToken cancellationToken)
{
foreach (var analyzerState in _analyzerStates)
{
if (!analyzerState.IsDeclarationComplete(decl))
var declarationComplete = await analyzerState.IsDeclarationCompleteAsync(decl, cancellationToken).ConfigureAwait(false);
if (!declarationComplete)
{
return false;
}
......@@ -583,19 +586,19 @@ public bool IsDeclarationComplete(SyntaxNode decl)
/// <summary>
/// Marks the given symbol declaration as fully analyzed for the given analyzer.
/// </summary>
public void MarkDeclarationComplete(SyntaxReference decl, DiagnosticAnalyzer analyzer)
public async Task MarkDeclarationCompleteAsync(SyntaxReference decl, DiagnosticAnalyzer analyzer, CancellationToken cancellationToken)
{
GetAnalyzerState(analyzer).MarkDeclarationComplete(decl);
await GetAnalyzerState(analyzer).MarkDeclarationCompleteAsync(decl, cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// Marks all the symbol declarations for the given symbol as fully analyzed for all the given analyzers.
/// </summary>
public void MarkDeclarationsComplete(ImmutableArray<SyntaxReference> declarations, IEnumerable<DiagnosticAnalyzer> analyzers)
public async Task MarkDeclarationsCompleteAsync(ImmutableArray<SyntaxReference> declarations, IEnumerable<DiagnosticAnalyzer> analyzers, CancellationToken cancellationToken)
{
foreach (var analyzer in analyzers)
{
GetAnalyzerState(analyzer).MarkDeclarationsComplete(declarations);
await GetAnalyzerState(analyzer).MarkDeclarationsCompleteAsync(declarations, cancellationToken).ConfigureAwait(false);
}
}
......@@ -603,20 +606,20 @@ public void MarkDeclarationsComplete(ImmutableArray<SyntaxReference> declaration
/// Attempts to start processing a syntax tree for the given analyzer's syntax tree actions.
/// </summary>
/// <returns>
/// Returns false if the tree has already been processed for the analyzer OR is currently being processed by another task.
/// If true, then it returns a non-null <paramref name="state"/> representing partial syntax analysis state for the given tree for the given analyzer.
/// Returns null if the tree has already been processed for the analyzer OR is currently being processed by another task.
/// Otherwise, returns a non-null state representing partial syntax analysis state for the given tree for the given analyzer.
/// </returns>
public bool TryStartSyntaxAnalysis(SyntaxTree tree, DiagnosticAnalyzer analyzer, out AnalyzerStateData state)
public async Task<AnalyzerStateData> TryStartSyntaxAnalysisAsync(SyntaxTree tree, DiagnosticAnalyzer analyzer, CancellationToken cancellationToken)
{
return GetAnalyzerState(analyzer).TryStartSyntaxAnalysis(tree, out state);
return await GetAnalyzerState(analyzer).TryStartSyntaxAnalysisAsync(tree, cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// Marks the given tree as fully syntactically analyzed for the given analyzer.
/// </summary>
public void MarkSyntaxAnalysisComplete(SyntaxTree tree, DiagnosticAnalyzer analyzer)
public async Task MarkSyntaxAnalysisCompleteAsync(SyntaxTree tree, DiagnosticAnalyzer analyzer, CancellationToken cancellationToken)
{
GetAnalyzerState(analyzer).MarkSyntaxAnalysisComplete(tree);
await GetAnalyzerState(analyzer).MarkSyntaxAnalysisCompleteAsync(tree, cancellationToken).ConfigureAwait(false);
}
}
}
......@@ -55,10 +55,10 @@ internal partial class AnalyzerManager
{
Func<Compilation, Task<HostCompilationStartAnalysisScope>> getTask = comp =>
{
return Task.Run(() =>
return Task.Run(async() =>
{
var compilationAnalysisScope = new HostCompilationStartAnalysisScope(sessionScope);
analyzerExecutor.ExecuteCompilationStartActions(sessionScope.CompilationStartActions, compilationAnalysisScope);
await analyzerExecutor.ExecuteCompilationStartActionsAsync(sessionScope.CompilationStartActions, compilationAnalysisScope).ConfigureAwait(false);
return compilationAnalysisScope;
}, analyzerExecutor.CancellationToken);
};
......@@ -100,10 +100,10 @@ internal partial class AnalyzerManager
{
Func<DiagnosticAnalyzer, Task<HostSessionStartAnalysisScope>> getTask = a =>
{
return Task.Run(() =>
return Task.Run(async() =>
{
var sessionScope = new HostSessionStartAnalysisScope();
analyzerExecutor.ExecuteInitializeMethod(a, sessionScope);
await analyzerExecutor.ExecuteInitializeMethodAsync(a, sessionScope).ConfigureAwait(false);
return sessionScope;
}, analyzerExecutor.CancellationToken);
};
......@@ -160,14 +160,14 @@ public async Task<AnalyzerActions> GetAnalyzerActionsAsync(DiagnosticAnalyzer an
var supportedDiagnostics = ImmutableArray<DiagnosticDescriptor>.Empty;
// Catch Exception from analyzer.SupportedDiagnostics
analyzerExecutor.ExecuteAndCatchIfThrows(analyzer, () =>
analyzerExecutor.ExecuteAndCatchIfThrowsAsync(analyzer, () =>
{
var supportedDiagnosticsLocal = analyzer.SupportedDiagnostics;
if (!supportedDiagnosticsLocal.IsDefaultOrEmpty)
{
supportedDiagnostics = supportedDiagnosticsLocal;
}
});
}).Wait(analyzerExecutor.CancellationToken);
EventHandler<Exception> handler = null;
Action<Exception, DiagnosticAnalyzer, Diagnostic> onAnalyzerException = analyzerExecutor.OnAnalyzerException;
......
......@@ -68,7 +68,7 @@ public class CompilationWithAnalyzers
/// Pool of event queues to serve each diagnostics request.
/// </summary>
private static readonly ObjectPool<AsyncQueue<CompilationEvent>> s_eventQueuePool = new ObjectPool<AsyncQueue<CompilationEvent>>(() => new AsyncQueue<CompilationEvent>());
private static readonly AsyncQueue<CompilationEvent> s_EmptyEventQueue = new AsyncQueue<CompilationEvent>();
private static readonly Task<AsyncQueue<CompilationEvent>> s_EmptyEventQueueTask = Task.FromResult(new AsyncQueue<CompilationEvent>());
/// <summary>
......@@ -362,11 +362,11 @@ private async Task<ImmutableArray<Diagnostic>> GetAnalyzerDiagnosticsCoreAsync(I
}
};
Func<AsyncQueue<CompilationEvent>> getEventQueue = () =>
GetPendingEvents(analyzers, includeSourceEvents, includeNonSourceEvents);
Func<Task<AsyncQueue<CompilationEvent>>> getEventQueueTask = () =>
GetPendingEventsAsync(analyzers, includeSourceEvents, includeNonSourceEvents, cancellationToken);
// Compute the analyzer diagnostics for the given analysis scope.
await ComputeAnalyzerDiagnosticsAsync(analysisScope, generateCompilationEvents, getEventQueue, newTaskToken: 0, cancellationToken: cancellationToken).ConfigureAwait(false);
await ComputeAnalyzerDiagnosticsAsync(analysisScope, generateCompilationEvents, getEventQueueTask, newTaskToken: 0, cancellationToken: cancellationToken).ConfigureAwait(false);
// Return computed analyzer diagnostics for the given analysis scope.
var analyzerDiagnostics = _analysisResult.GetDiagnostics(analysisScope, getLocalDiagnostics: includeSourceEvents, getNonLocalDiagnostics: includeNonSourceEvents);
......@@ -420,7 +420,7 @@ private async Task<ImmutableArray<Diagnostic>> GetAnalyzerSyntaxDiagnosticsCoreA
var analysisScope = new AnalysisScope(analyzers, tree, filterSpan: null, syntaxAnalysis: true, concurrentAnalysis: _analysisOptions.ConcurrentAnalysis, categorizeDiagnostics: true);
Action generateCompilationEvents = null;
Func<AsyncQueue<CompilationEvent>> getEventQueue = () => s_EmptyEventQueue;
Func<Task<AsyncQueue<CompilationEvent>>> getEventQueue = () => s_EmptyEventQueueTask;
// Compute the analyzer diagnostics for the given analysis scope.
await ComputeAnalyzerDiagnosticsAsync(analysisScope, generateCompilationEvents, getEventQueue, taskToken, cancellationToken).ConfigureAwait(false);
......@@ -487,14 +487,15 @@ private async Task<ImmutableArray<Diagnostic>> GetAnalyzerSemanticDiagnosticsCor
Action generateCompilationEvents = () =>
AnalyzerDriver.GetOrCreateCachedSemanticModel(model.SyntaxTree, _compilation, cancellationToken);
Func<AsyncQueue<CompilationEvent>> getEventQueue = () => GetPendingEvents(analyzers, model.SyntaxTree);
Func<Task<AsyncQueue<CompilationEvent>>> getEventQueueTask = () => GetPendingEventsAsync(analyzers, model.SyntaxTree, cancellationToken);
// Compute the analyzer diagnostics for the given analysis scope.
// We need to loop till symbol analysis is complete for any partial symbols being processed for other tree diagnostic requests.
do
{
await ComputeAnalyzerDiagnosticsAsync(analysisScope, generateCompilationEvents, getEventQueue, taskToken, cancellationToken).ConfigureAwait(false);
} while (_analysisOptions.ConcurrentAnalysis && _analysisState.HasPendingSymbolAnalysis(analysisScope));
await ComputeAnalyzerDiagnosticsAsync(analysisScope, generateCompilationEvents, getEventQueueTask, taskToken, cancellationToken).ConfigureAwait(false);
} while (_analysisOptions.ConcurrentAnalysis &&
(await _analysisState.HasPendingSymbolAnalysisAsync(analysisScope, cancellationToken).ConfigureAwait(false)));
// Return computed analyzer diagnostics for the given analysis scope.
return _analysisResult.GetDiagnostics(analysisScope, getLocalDiagnostics: true, getNonLocalDiagnostics: false);
......@@ -505,7 +506,7 @@ private async Task<ImmutableArray<Diagnostic>> GetAnalyzerSemanticDiagnosticsCor
}
}
private async Task ComputeAnalyzerDiagnosticsAsync(AnalysisScope analysisScope, Action generateCompilationEventsOpt, Func<AsyncQueue<CompilationEvent>> getEventQueue, int newTaskToken, CancellationToken cancellationToken)
private async Task ComputeAnalyzerDiagnosticsAsync(AnalysisScope analysisScope, Action generateCompilationEventsOpt, Func<Task<AsyncQueue<CompilationEvent>>> getEventQueueTask, int newTaskToken, CancellationToken cancellationToken)
{
try
{
......@@ -552,11 +553,11 @@ private async Task ComputeAnalyzerDiagnosticsAsync(AnalysisScope analysisScope,
AsyncQueue<CompilationEvent> eventQueue = null;
try
{
// Get event queue with pending events to analyze.
eventQueue = getEventQueue();
// Get event queue with pending events to analyze.
eventQueue = await getEventQueueTask().ConfigureAwait(false);
// Execute analyzer driver on the given analysis scope with the given event queue.
await ComputeAnalyzerDiagnosticsCoreAsync(driver, eventQueue, analysisScope, cancellationToken: linkedCts.Token).ConfigureAwait(false);
// Execute analyzer driver on the given analysis scope with the given event queue.
await ComputeAnalyzerDiagnosticsCoreAsync(driver, eventQueue, analysisScope, cancellationToken: linkedCts.Token).ConfigureAwait(false);
}
finally
{
......@@ -568,7 +569,7 @@ private async Task ComputeAnalyzerDiagnosticsAsync(AnalysisScope analysisScope,
throw ExceptionUtilities.Unreachable;
}
},
linkedCts.Token),
linkedCts.Token),
cts);
// Wait for higher priority tree document tasks to complete.
......@@ -671,7 +672,7 @@ private async Task ComputeAnalyzerDiagnosticsCoreAsync(AnalyzerDriver driver, As
{
Debug.Assert(!driver.WhenInitializedTask.IsCanceled);
if (eventQueue.Count > 0 || _analysisState.HasPendingSyntaxAnalysis(analysisScope))
if (eventQueue.Count > 0 || (await _analysisState.HasPendingSyntaxAnalysisAsync(analysisScope, cancellationToken).ConfigureAwait(false)))
{
try
{
......@@ -891,13 +892,14 @@ private ImmutableArray<CompilationEvent> DequeueGeneratedCompilationEvents()
return builder.ToImmutable();
}
private AsyncQueue<CompilationEvent> GetPendingEvents(ImmutableArray<DiagnosticAnalyzer> analyzers, SyntaxTree tree)
private async Task<AsyncQueue<CompilationEvent>> GetPendingEventsAsync(ImmutableArray<DiagnosticAnalyzer> analyzers, SyntaxTree tree, CancellationToken cancellationToken)
{
var eventQueue = s_eventQueuePool.Allocate();
Debug.Assert(!eventQueue.IsCompleted);
Debug.Assert(eventQueue.Count == 0);
foreach (var compilationEvent in _analysisState.GetPendingEvents(analyzers, tree))
var pendingEvents = await _analysisState.GetPendingEventsAsync(analyzers, tree, cancellationToken).ConfigureAwait(false);
foreach (var compilationEvent in pendingEvents)
{
eventQueue.Enqueue(compilationEvent);
}
......@@ -905,7 +907,7 @@ private AsyncQueue<CompilationEvent> GetPendingEvents(ImmutableArray<DiagnosticA
return eventQueue;
}
private AsyncQueue<CompilationEvent> GetPendingEvents(ImmutableArray<DiagnosticAnalyzer> analyzers, bool includeSourceEvents, bool includeNonSourceEvents)
private async Task<AsyncQueue<CompilationEvent>> GetPendingEventsAsync(ImmutableArray<DiagnosticAnalyzer> analyzers, bool includeSourceEvents, bool includeNonSourceEvents, CancellationToken cancellationToken)
{
Debug.Assert(includeSourceEvents || includeNonSourceEvents);
......@@ -913,7 +915,8 @@ private AsyncQueue<CompilationEvent> GetPendingEvents(ImmutableArray<DiagnosticA
Debug.Assert(!eventQueue.IsCompleted);
Debug.Assert(eventQueue.Count == 0);
foreach (var compilationEvent in _analysisState.GetPendingEvents(analyzers, includeSourceEvents, includeNonSourceEvents))
var pendingEvents = await _analysisState.GetPendingEventsAsync(analyzers, includeSourceEvents, includeNonSourceEvents, cancellationToken).ConfigureAwait(false);
foreach (var compilationEvent in pendingEvents)
{
eventQueue.Enqueue(compilationEvent);
}
......@@ -923,7 +926,7 @@ private AsyncQueue<CompilationEvent> GetPendingEvents(ImmutableArray<DiagnosticA
private void FreeEventQueue(AsyncQueue<CompilationEvent> eventQueue)
{
if (eventQueue == null || ReferenceEquals(eventQueue, s_EmptyEventQueue))
if (eventQueue == null || ReferenceEquals(eventQueue, s_EmptyEventQueueTask.Result))
{
return;
}
......
......@@ -118,6 +118,12 @@
<Compile Include="..\..\..\Compilers\Core\Portable\InternalUtilities\ReferenceEqualityComparer.cs">
<Link>InternalUtilities\ReferenceEqualityComparer.cs</Link>
</Compile>
<Compile Include="..\..\..\Compilers\Core\Portable\InternalUtilities\SemaphoreSlimExtensions.cs">
<Link>InternalUtilities\SemaphoreSlimExtensions.cs</Link>
</Compile>
<Compile Include="..\..\..\Compilers\Core\Portable\InternalUtilities\SemaphoreSlimFactory.cs">
<Link>InternalUtilities\SemaphoreSlimFactory.cs</Link>
</Compile>
<Compile Include="..\..\..\Compilers\Core\Portable\InternalUtilities\SpecializedCollections.cs">
<Link>InternalUtilities\SpecializedCollections.cs</Link>
</Compile>
......@@ -787,7 +793,6 @@
<Compile Include="Utilities\AbstractSpeculationAnalyzer.cs" />
<Compile Include="Utilities\AnnotationTable.cs" />
<Compile Include="Utilities\AsyncLazy`1.cs" />
<Compile Include="Utilities\SemaphoreSlimFactory.cs" />
<Compile Include="Utilities\BidirectionalMap.cs" />
<Compile Include="Utilities\CancellableLazy.cs" />
<Compile Include="Utilities\CancellableLazy`1.cs" />
......@@ -806,7 +811,6 @@
<Compile Include="Utilities\LazyInitialization.cs" />
<Compile Include="Utilities\ObjectPools\Extensions.cs" />
<Compile Include="Utilities\ObjectPools\SharedPools.cs" />
<Compile Include="Utilities\SemaphoreSlimExtensions.cs" />
<Compile Include="Utilities\SerializableBytes.cs" />
<Compile Include="Utilities\SimpleTaskQueue.cs" />
<Compile Include="Utilities\SpecializedTasks.cs" />
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册