From 34460a3c46f635305d24775dd5f097a8d179e145 Mon Sep 17 00:00:00 2001 From: Manish Vasani Date: Thu, 19 Nov 2015 11:04:03 -0800 Subject: [PATCH] [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 --- .../Core/Portable/CodeAnalysis.csproj | 2 + .../AnalysisState.PerAnalyzerState.cs | 86 +++--- .../DiagnosticAnalyzer/AnalysisState.cs | 107 ++++---- .../DiagnosticAnalyzer/AnalyzerDriver.cs | 125 ++++----- .../DiagnosticAnalyzer/AnalyzerExecutor.cs | 255 ++++++++++-------- .../DiagnosticAnalyzer/AnalyzerManager.cs | 12 +- .../CompilationWithAnalyzers.cs | 43 +-- .../SemaphoreSlimExtensions.cs | 0 .../SemaphoreSlimFactory.cs | 0 .../Core/Portable/Workspaces.csproj | 8 +- 10 files changed, 343 insertions(+), 295 deletions(-) rename src/{Workspaces/Core/Portable/Utilities => Compilers/Core/Portable/InternalUtilities}/SemaphoreSlimExtensions.cs (100%) rename src/{Workspaces/Core/Portable/Utilities => Compilers/Core/Portable/InternalUtilities}/SemaphoreSlimFactory.cs (100%) diff --git a/src/Compilers/Core/Portable/CodeAnalysis.csproj b/src/Compilers/Core/Portable/CodeAnalysis.csproj index 659fe4863c3..7ca44f87f0a 100644 --- a/src/Compilers/Core/Portable/CodeAnalysis.csproj +++ b/src/Compilers/Core/Portable/CodeAnalysis.csproj @@ -184,6 +184,8 @@ + + diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalysisState.PerAnalyzerState.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalysisState.PerAnalyzerState.cs index 5653cbae962..3b2fa7c0b77 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalysisState.PerAnalyzerState.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalysisState.PerAnalyzerState.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 _pendingEvents = new Dictionary(); private readonly Dictionary _pendingSymbols = new Dictionary(); private readonly Dictionary _pendingDeclarations = new Dictionary(); @@ -33,35 +35,36 @@ public PerAnalyzerState(ObjectPool analyzerStateDataPool, Obj public IEnumerable PendingEvents_NoLock => _pendingEvents.Keys; - public bool HasPendingSyntaxAnalysis(SyntaxTree treeOpt) + public async Task 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 HasPendingSymbolAnalysisAsync(ISymbol symbol, CancellationToken cancellationToken) { - lock (_gate) + using (await _gate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false)) { return _pendingSymbols.ContainsKey(symbol); } } - private bool TryStartProcessingEntity(TAnalysisEntity analysisEntity, Dictionary pendingEntities, ObjectPool pool, out TAnalyzerStateData newState) + private async Task TryStartProcessingEntityAsync(TAnalysisEntity analysisEntity, Dictionary pendingEntities, ObjectPool 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 analysisEntity, Dictionary pendingEntities, ObjectPool pool, out TAnalyzerStateData state) + private static TAnalyzerStateData TryStartProcessingEntity_NoLock(TAnalysisEntity analysisEntity, Dictionary pendingEntities, ObjectPool 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 analysisEntity, Dictionary pendingEntities, ObjectPool pool) + private async Task MarkEntityProcessedAsync(TAnalysisEntity analysisEntity, Dictionary pendingEntities, ObjectPool 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 analysisEntity, Dictionary pendingEntities) + private async Task IsEntityFullyProcessedAsync(TAnalysisEntity analysisEntity, Dictionary 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 Task TryStartProcessingEventAsync(CompilationEvent compilationEvent, CancellationToken cancellationToken) { - return TryStartProcessingEntity(compilationEvent, _pendingEvents, _analyzerStateDataPool, out state); + return TryStartProcessingEntityAsync(compilationEvent, _pendingEvents, _analyzerStateDataPool, cancellationToken); } - public void MarkEventComplete(CompilationEvent compilationEvent) + public Task MarkEventCompleteAsync(CompilationEvent compilationEvent, CancellationToken cancellationToken) { - MarkEntityProcessed(compilationEvent, _pendingEvents, _analyzerStateDataPool); + return MarkEntityProcessedAsync(compilationEvent, _pendingEvents, _analyzerStateDataPool, cancellationToken); } - public bool TryStartAnalyzingSymbol(ISymbol symbol, out AnalyzerStateData state) + public Task TryStartAnalyzingSymbolAsync(ISymbol symbol, CancellationToken cancellationToken) { - return TryStartProcessingEntity(symbol, _pendingSymbols, _analyzerStateDataPool, out state); + return TryStartProcessingEntityAsync(symbol, _pendingSymbols, _analyzerStateDataPool, cancellationToken); } - public void MarkSymbolComplete(ISymbol symbol) + public Task MarkSymbolCompleteAsync(ISymbol symbol, CancellationToken cancellationToken) { - MarkEntityProcessed(symbol, _pendingSymbols, _analyzerStateDataPool); + return MarkEntityProcessedAsync(symbol, _pendingSymbols, _analyzerStateDataPool, cancellationToken); } - public bool TryStartAnalyzingDeclaration(SyntaxReference decl, out DeclarationAnalyzerStateData state) + public Task TryStartAnalyzingDeclarationAsync(SyntaxReference decl, CancellationToken cancellationToken) { - return TryStartProcessingEntity(decl.GetSyntax(), _pendingDeclarations, _declarationAnalyzerStateDataPool, out state); + return TryStartProcessingEntityAsync(decl.GetSyntax(), _pendingDeclarations, _declarationAnalyzerStateDataPool, cancellationToken); } - public bool IsDeclarationComplete(SyntaxNode decl) + public Task IsDeclarationCompleteAsync(SyntaxNode decl, CancellationToken cancellationToken) { - return IsEntityFullyProcessed(decl, _pendingDeclarations); + return IsEntityFullyProcessedAsync(decl, _pendingDeclarations, cancellationToken); } - public void MarkDeclarationComplete(SyntaxReference decl) + public Task MarkDeclarationCompleteAsync(SyntaxReference decl, CancellationToken cancellationToken) { - MarkEntityProcessed(decl.GetSyntax(), _pendingDeclarations, _declarationAnalyzerStateDataPool); + return MarkEntityProcessedAsync(decl.GetSyntax(), _pendingDeclarations, _declarationAnalyzerStateDataPool, cancellationToken); } - public bool TryStartSyntaxAnalysis(SyntaxTree tree, out AnalyzerStateData state) + public Task TryStartSyntaxAnalysisAsync(SyntaxTree tree, CancellationToken cancellationToken) { Debug.Assert(_lazyPendingSyntaxAnalysisTrees != null); - return TryStartProcessingEntity(tree, _lazyPendingSyntaxAnalysisTrees, _analyzerStateDataPool, out state); + return TryStartProcessingEntityAsync(tree, _lazyPendingSyntaxAnalysisTrees, _analyzerStateDataPool, cancellationToken); } - 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 declarations) + public async Task MarkDeclarationsCompleteAsync(ImmutableArray 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 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 Task IsEventAnalyzedAsync(CompilationEvent compilationEvent, CancellationToken cancellationToken) { - return IsEntityFullyProcessed(compilationEvent, _pendingEvents); + return IsEntityFullyProcessedAsync(compilationEvent, _pendingEvents, cancellationToken); } - 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); } diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalysisState.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalysisState.cs index ac210699cb6..60617d52973 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalysisState.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalysisState.cs @@ -18,7 +18,7 @@ namespace Microsoft.CodeAnalysis.Diagnostics /// internal partial class AnalysisState { - private readonly object _gate; + private readonly SemaphoreSlim _gate; /// /// Per-analyzer analysis state map. @@ -60,7 +60,7 @@ internal partial class AnalysisState public AnalysisState(ImmutableArray analyzers) { - _gate = new object(); + _gate = new SemaphoreSlim(initialCount: 1); _analyzerStateMap = CreateAnalyzerStateMap(analyzers, out _analyzerStates); _pendingSourceEvents = new Dictionary>(); _pendingNonSourceEvents = new HashSet(); @@ -100,13 +100,13 @@ public async Task OnCompilationEventsGeneratedAsync(ImmutableArray compilationEvents, AnalyzerDriver driver, CancellationToken cancellationToken) + private async Task OnCompilationEventsGenerated_NoLockAsync(ImmutableArray compilationEvents, AnalyzerDriver driver, CancellationToken cancellationToken) { Debug.Assert(_lazyAnalyzerActionCountsMap != null); @@ -126,7 +126,7 @@ private void OnCompilationEventsGenerated_NoLock(ImmutableArray analyzers) + private async Task OnSymbolDeclaredEventProcessedAsync(SymbolDeclaredCompilationEvent symbolDeclaredEvent, ImmutableArray 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. /// - 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 /// /// Gets pending events for given set of analyzers for the given syntax tree. /// - public ImmutableArray GetPendingEvents(ImmutableArray analyzers, SyntaxTree tree) + public async Task> GetPendingEventsAsync(ImmutableArray 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 GetPendingEvents_NoLock(ImmutableArray< /// /// Indicates if source events (symbol declared, compilation unit completed event) should be included. /// Indicates if compilation wide events (compilation started and completed event) should be included. - public ImmutableArray GetPendingEvents(ImmutableArray analyzers, bool includeSourceEvents, bool includeNonSourceEvents) + /// Cancellation token. + public async Task> GetPendingEventsAsync(ImmutableArray 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 events) /// /// Returns true if we have any pending syntax analysis for given analysis scope. /// - public bool HasPendingSyntaxAnalysis(AnalysisScope analysisScope) + public async Task 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) /// /// Returns true if we have any pending symbol analysis for given analysis scope. /// - public bool HasPendingSymbolAnalysis(AnalysisScope analysisScope) + public async Task 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 GetPendingSymbolDeclaredEvents(SyntaxTree tree) + private async Task> GetPendingSymbolDeclaredEventsAsync(SyntaxTree tree, CancellationToken cancellationToken) { Debug.Assert(tree != null); - lock (_gate) + using (await _gate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false)) { HashSet compilationEvents; if (!_pendingSourceEvents.TryGetValue(tree, out compilationEvents)) @@ -516,62 +518,63 @@ private ImmutableArray GetPendingSymbolDeclaredE /// Attempts to start processing a compilation event for the given analyzer. /// /// - /// 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 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. /// - public bool TryStartProcessingEvent(CompilationEvent compilationEvent, DiagnosticAnalyzer analyzer, out AnalyzerStateData state) + public Task TryStartProcessingEventAsync(CompilationEvent compilationEvent, DiagnosticAnalyzer analyzer, CancellationToken cancellationToken) { - return GetAnalyzerState(analyzer).TryStartProcessingEvent(compilationEvent, out state); + return GetAnalyzerState(analyzer).TryStartProcessingEventAsync(compilationEvent, cancellationToken); } /// /// Marks the given event as fully analyzed for the given analyzer. /// - public void MarkEventComplete(CompilationEvent compilationEvent, DiagnosticAnalyzer analyzer) + public Task MarkEventCompleteAsync(CompilationEvent compilationEvent, DiagnosticAnalyzer analyzer, CancellationToken cancellationToken) { - GetAnalyzerState(analyzer).MarkEventComplete(compilationEvent); + return GetAnalyzerState(analyzer).MarkEventCompleteAsync(compilationEvent, cancellationToken); } /// /// Attempts to start processing a symbol for the given analyzer's symbol actions. /// /// - /// 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 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. /// - public bool TryStartAnalyzingSymbol(ISymbol symbol, DiagnosticAnalyzer analyzer, out AnalyzerStateData state) + public Task TryStartAnalyzingSymbolAsync(ISymbol symbol, DiagnosticAnalyzer analyzer, CancellationToken cancellationToken) { - return GetAnalyzerState(analyzer).TryStartAnalyzingSymbol(symbol, out state); + return GetAnalyzerState(analyzer).TryStartAnalyzingSymbolAsync(symbol, cancellationToken); } /// /// Marks the given symbol as fully analyzed for the given analyzer. /// - public void MarkSymbolComplete(ISymbol symbol, DiagnosticAnalyzer analyzer) + public Task MarkSymbolCompleteAsync(ISymbol symbol, DiagnosticAnalyzer analyzer, CancellationToken cancellationToken) { - GetAnalyzerState(analyzer).MarkSymbolComplete(symbol); + return GetAnalyzerState(analyzer).MarkSymbolCompleteAsync(symbol, cancellationToken); } /// /// Attempts to start processing a symbol declaration for the given analyzer's syntax node and code block actions. /// /// - /// 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 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. /// - public bool TryStartAnalyzingDeclaration(SyntaxReference decl, DiagnosticAnalyzer analyzer, out DeclarationAnalyzerStateData state) + public Task TryStartAnalyzingDeclarationAsync(SyntaxReference decl, DiagnosticAnalyzer analyzer, CancellationToken cancellationToken) { - return GetAnalyzerState(analyzer).TryStartAnalyzingDeclaration(decl, out state); + return GetAnalyzerState(analyzer).TryStartAnalyzingDeclarationAsync(decl, cancellationToken); } /// - /// True if the given symbol declaration is fully analyzed. + /// Returns true if the given symbol declaration is fully analyzed. /// - public bool IsDeclarationComplete(SyntaxNode decl) + public async Task 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) /// /// Marks the given symbol declaration as fully analyzed for the given analyzer. /// - public void MarkDeclarationComplete(SyntaxReference decl, DiagnosticAnalyzer analyzer) + public Task MarkDeclarationCompleteAsync(SyntaxReference decl, DiagnosticAnalyzer analyzer, CancellationToken cancellationToken) { - GetAnalyzerState(analyzer).MarkDeclarationComplete(decl); + return GetAnalyzerState(analyzer).MarkDeclarationCompleteAsync(decl, cancellationToken); } /// /// Marks all the symbol declarations for the given symbol as fully analyzed for all the given analyzers. /// - public void MarkDeclarationsComplete(ImmutableArray declarations, IEnumerable analyzers) + public async Task MarkDeclarationsCompleteAsync(ImmutableArray declarations, IEnumerable 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 declaration /// Attempts to start processing a syntax tree for the given analyzer's syntax tree actions. /// /// - /// 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 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. /// - public bool TryStartSyntaxAnalysis(SyntaxTree tree, DiagnosticAnalyzer analyzer, out AnalyzerStateData state) + public Task TryStartSyntaxAnalysisAsync(SyntaxTree tree, DiagnosticAnalyzer analyzer, CancellationToken cancellationToken) { - return GetAnalyzerState(analyzer).TryStartSyntaxAnalysis(tree, out state); + return GetAnalyzerState(analyzer).TryStartSyntaxAnalysisAsync(tree, cancellationToken); } /// /// Marks the given tree as fully syntactically analyzed for the given analyzer. /// - public void MarkSyntaxAnalysisComplete(SyntaxTree tree, DiagnosticAnalyzer analyzer) + public Task MarkSyntaxAnalysisCompleteAsync(SyntaxTree tree, DiagnosticAnalyzer analyzer, CancellationToken cancellationToken) { - GetAnalyzerState(analyzer).MarkSyntaxAnalysisComplete(tree); + return GetAnalyzerState(analyzer).MarkSyntaxAnalysisCompleteAsync(tree, cancellationToken); } } } diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs index c2f4d0646f7..e8efc02f6f6 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs @@ -8,7 +8,6 @@ using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.Semantics; using Microsoft.CodeAnalysis.Diagnostics.Telemetry; using Roslyn.Utilities; @@ -160,7 +159,7 @@ private void Initialize(AnalyzerExecutor analyzerExecutor, DiagnosticQueue diagn } // Assume all analyzers are non-thread safe. - var singleThreadedAnalyzerToGateMap = ImmutableDictionary.CreateRange(analyzers.Select(a => KeyValuePair.Create(a, new object()))); + var singleThreadedAnalyzerToGateMap = ImmutableDictionary.CreateRange(analyzers.Select(a => KeyValuePair.Create(a, new SemaphoreSlim(initialCount: 1)))); if (analysisOptions.LogAnalyzerExecutionTime) { @@ -169,7 +168,7 @@ private void Initialize(AnalyzerExecutor analyzerExecutor, DiagnosticQueue diagn var unused = compilation.GetTypeByMetadataName("System.Object"); } - Func getAnalyzerGate = analyzer => singleThreadedAnalyzerToGateMap[analyzer]; + Func getAnalyzerGate = analyzer => singleThreadedAnalyzerToGateMap[analyzer]; var analyzerExecutor = AnalyzerExecutor.Create(compilation, analysisOptions.Options ?? AnalyzerOptions.Empty, addDiagnostic, newOnAnalyzerException, IsCompilerAnalyzer, analyzerManager, getAnalyzerGate, analysisOptions.LogAnalyzerExecutionTime, addLocalDiagnosticOpt, addNonLocalDiagnosticOpt, cancellationToken); @@ -287,7 +286,7 @@ private static void OnDriverException(Task faultedTask, AnalyzerExecutor analyze analyzerExecutor.OnAnalyzerException(innerException, analyzer, diagnostic); } - private void ExecuteSyntaxTreeActions(AnalysisScope analysisScope, AnalysisState analysisStateOpt, CancellationToken cancellationToken) + private async Task ExecuteSyntaxTreeActionsAsync(AnalysisScope analysisScope, AnalysisState analysisStateOpt, CancellationToken cancellationToken) { if (analysisScope.IsTreeAnalysis && !analysisScope.IsSyntaxOnlyTreeAnalysis) { @@ -305,11 +304,11 @@ private void ExecuteSyntaxTreeActions(AnalysisScope analysisScope, AnalysisState if (_syntaxTreeActionsMap.TryGetValue(analyzer, out syntaxTreeActions)) { // Execute actions for a given analyzer sequentially. - analyzerExecutor.ExecuteSyntaxTreeActions(syntaxTreeActions, analyzer, tree, analysisScope, analysisStateOpt); + await analyzerExecutor.ExecuteSyntaxTreeActionsAsync(syntaxTreeActions, analyzer, tree, analysisScope, analysisStateOpt).ConfigureAwait(false); } - else + else if (analysisStateOpt != null) { - analysisStateOpt?.MarkSyntaxAnalysisComplete(tree, analyzer); + await analysisStateOpt.MarkSyntaxAnalysisCompleteAsync(tree, analyzer, cancellationToken).ConfigureAwait(false); } } } @@ -397,7 +396,7 @@ public async Task> GetDiagnosticsAsync(Compilation co if (reportSuppressedDiagnostics || !d.IsSuppressed) { allDiagnostics.Add(d); - } + } } return allDiagnostics.ToReadOnlyAndFree(); @@ -537,7 +536,7 @@ private async Task ProcessCompilationEventsAsync(AnalysisScope analysisScope, An cancellationToken.ThrowIfCancellationRequested(); // Kick off tasks to execute syntax tree actions. - var syntaxTreeActionsTask = Task.Run(() => ExecuteSyntaxTreeActions(analysisScope, analysisStateOpt, cancellationToken)); + var syntaxTreeActionsTask = ExecuteSyntaxTreeActionsAsync(analysisScope, analysisStateOpt, cancellationToken); // Wait for all worker threads to complete processing events. await Task.WhenAll(workerTasks.Concat(syntaxTreeActionsTask)).ConfigureAwait(false); @@ -555,13 +554,13 @@ private async Task ProcessCompilationEventsAsync(AnalysisScope analysisScope, An { completedEvent = await ProcessCompilationEventsCoreAsync(analysisScope, analysisStateOpt, prePopulatedEventQueue, cancellationToken).ConfigureAwait(false); - ExecuteSyntaxTreeActions(analysisScope, analysisStateOpt, cancellationToken); + await ExecuteSyntaxTreeActionsAsync(analysisScope, analysisStateOpt, cancellationToken).ConfigureAwait(false); } // Finally process the compilation completed event, if any. if (completedEvent != null) { - ProcessEvent(completedEvent, analysisScope, analysisStateOpt, cancellationToken); + await ProcessEventAsync(completedEvent, analysisScope, analysisStateOpt, cancellationToken).ConfigureAwait(false); } } catch (Exception e) when (FatalError.ReportUnlessCanceled(e)) @@ -618,7 +617,7 @@ private async Task ProcessCompilationEventsCoreAsync( continue; } - ProcessEvent(e, analysisScope, analysisStateOpt, cancellationToken); + await ProcessEventAsync(e, analysisScope, analysisStateOpt, cancellationToken).ConfigureAwait(false); } return completedEvent; @@ -629,48 +628,52 @@ private async Task ProcessCompilationEventsCoreAsync( } } - private void ProcessEvent(CompilationEvent e, AnalysisScope analysisScope, AnalysisState analysisStateOpt, CancellationToken cancellationToken) + private async Task ProcessEventAsync(CompilationEvent e, AnalysisScope analysisScope, AnalysisState analysisStateOpt, CancellationToken cancellationToken) { - ProcessEventCore(e, analysisScope, analysisStateOpt, cancellationToken); - analysisStateOpt?.OnCompilationEventProcessed(e, analysisScope); + await ProcessEventCoreAsync(e, analysisScope, analysisStateOpt, cancellationToken).ConfigureAwait(false); + + if (analysisStateOpt != null) + { + await analysisStateOpt.OnCompilationEventProcessedAsync(e, analysisScope, cancellationToken).ConfigureAwait(false); + } } - private void ProcessEventCore(CompilationEvent e, AnalysisScope analysisScope, AnalysisState analysisStateOpt, CancellationToken cancellationToken) + private async Task ProcessEventCoreAsync(CompilationEvent e, AnalysisScope analysisScope, AnalysisState analysisStateOpt, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); var symbolEvent = e as SymbolDeclaredCompilationEvent; if (symbolEvent != null) { - ProcessSymbolDeclared(symbolEvent, analysisScope, analysisStateOpt, cancellationToken); + await ProcessSymbolDeclaredAsync(symbolEvent, analysisScope, analysisStateOpt, cancellationToken).ConfigureAwait(false); return; } var completedEvent = e as CompilationUnitCompletedEvent; if (completedEvent != null) { - ProcessCompilationUnitCompleted(completedEvent, analysisScope, analysisStateOpt, cancellationToken); + await ProcessCompilationUnitCompletedAsync(completedEvent, analysisScope, analysisStateOpt, cancellationToken).ConfigureAwait(false); return; } var endEvent = e as CompilationCompletedEvent; if (endEvent != null) { - ProcessCompilationCompleted(endEvent, analysisScope, analysisStateOpt, cancellationToken); + await ProcessCompilationCompletedAsync(endEvent, analysisScope, analysisStateOpt, cancellationToken).ConfigureAwait(false); return; } var startedEvent = e as CompilationStartedEvent; if (startedEvent != null) { - ProcessCompilationStarted(startedEvent, analysisScope, analysisStateOpt, cancellationToken); + await ProcessCompilationStartedAsync(startedEvent, analysisScope, analysisStateOpt, cancellationToken).ConfigureAwait(false); return; } throw new InvalidOperationException("Unexpected compilation event of type " + e.GetType().Name); } - private void ProcessSymbolDeclared(SymbolDeclaredCompilationEvent symbolEvent, AnalysisScope analysisScope, AnalysisState analysisStateOpt, CancellationToken cancellationToken) + private async Task ProcessSymbolDeclaredAsync(SymbolDeclaredCompilationEvent symbolEvent, AnalysisScope analysisScope, AnalysisState analysisStateOpt, CancellationToken cancellationToken) { try { @@ -679,12 +682,12 @@ private void ProcessSymbolDeclared(SymbolDeclaredCompilationEvent symbolEvent, A var references = symbolEvent.DeclaringSyntaxReferences; if (!AnalysisScope.ShouldSkipSymbolAnalysis(symbolEvent)) { - ExecuteSymbolActions(symbolEvent, analysisScope, analysisStateOpt, cancellationToken); + await ExecuteSymbolActionsAsync(symbolEvent, analysisScope, analysisStateOpt, cancellationToken).ConfigureAwait(false); } if (!AnalysisScope.ShouldSkipDeclarationAnalysis(symbol)) { - ExecuteDeclaringReferenceActions(symbolEvent, analysisScope, analysisStateOpt, cancellationToken); + await ExecuteDeclaringReferenceActionsAsync(symbolEvent, analysisScope, analysisStateOpt, cancellationToken).ConfigureAwait(false); } } finally @@ -693,7 +696,7 @@ private void ProcessSymbolDeclared(SymbolDeclaredCompilationEvent symbolEvent, A } } - private void ExecuteSymbolActions(SymbolDeclaredCompilationEvent symbolEvent, AnalysisScope analysisScope, AnalysisState analysisStateOpt, CancellationToken cancellationToken) + private async Task ExecuteSymbolActionsAsync(SymbolDeclaredCompilationEvent symbolEvent, AnalysisScope analysisScope, AnalysisState analysisStateOpt, CancellationToken cancellationToken) { var symbol = symbolEvent.Symbol; if (!analysisScope.ShouldAnalyze(symbol)) @@ -707,11 +710,11 @@ private void ExecuteSymbolActions(SymbolDeclaredCompilationEvent symbolEvent, An ImmutableArray> actionsByKind; if (_symbolActionsByKind.TryGetValue(analyzer, out actionsByKind) && (int)symbol.Kind < actionsByKind.Length) { - analyzerExecutor.ExecuteSymbolActions(actionsByKind[(int)symbol.Kind], analyzer, symbol, GetTopmostNodeForAnalysis, analysisScope, analysisStateOpt); + await analyzerExecutor.ExecuteSymbolActionsAsync(actionsByKind[(int)symbol.Kind], analyzer, symbol, GetTopmostNodeForAnalysis, analysisScope, analysisStateOpt).ConfigureAwait(false); } - else + else if (analysisStateOpt != null) { - analysisStateOpt?.MarkSymbolComplete(symbol, analyzer); + await analysisStateOpt.MarkSymbolCompleteAsync(symbol, analyzer, cancellationToken).ConfigureAwait(false); } } } @@ -722,9 +725,9 @@ private static SyntaxNode GetTopmostNodeForAnalysis(ISymbol symbol, SyntaxRefere return model.GetTopmostNodeForDiagnosticAnalysis(symbol, syntaxReference.GetSyntax()); } - protected abstract void ExecuteDeclaringReferenceActions(SymbolDeclaredCompilationEvent symbolEvent, AnalysisScope analysisScope, AnalysisState analysisStateOpt, CancellationToken cancellationToken); + protected abstract Task ExecuteDeclaringReferenceActionsAsync(SymbolDeclaredCompilationEvent symbolEvent, AnalysisScope analysisScope, AnalysisState analysisStateOpt, CancellationToken cancellationToken); - private void ProcessCompilationUnitCompleted(CompilationUnitCompletedEvent completedEvent, AnalysisScope analysisScope, AnalysisState analysisStateOpt, CancellationToken cancellationToken) + private async Task ProcessCompilationUnitCompletedAsync(CompilationUnitCompletedEvent completedEvent, AnalysisScope analysisScope, AnalysisState analysisStateOpt, CancellationToken cancellationToken) { // When the compiler is finished with a compilation unit, we can run user diagnostics which // might want to ask the compiler for all the diagnostics in the source file, for example @@ -747,11 +750,11 @@ private void ProcessCompilationUnitCompleted(CompilationUnitCompletedEvent compl if (_semanticModelActionsMap.TryGetValue(analyzer, out semanticModelActions)) { // Execute actions for a given analyzer sequentially. - analyzerExecutor.ExecuteSemanticModelActions(semanticModelActions, analyzer, semanticModel, completedEvent, analysisScope, analysisStateOpt); + await analyzerExecutor.ExecuteSemanticModelActionsAsync(semanticModelActions, analyzer, semanticModel, completedEvent, analysisScope, analysisStateOpt).ConfigureAwait(false); } - else + else if (analysisStateOpt != null) { - analysisStateOpt?.MarkEventComplete(completedEvent, analyzer); + await analysisStateOpt.MarkEventCompleteAsync(completedEvent, analyzer, cancellationToken).ConfigureAwait(false); } } } @@ -761,17 +764,17 @@ private void ProcessCompilationUnitCompleted(CompilationUnitCompletedEvent compl } } - private void ProcessCompilationStarted(CompilationStartedEvent startedEvent, AnalysisScope analysisScope, AnalysisState analysisStateOpt, CancellationToken cancellationToken) + private Task ProcessCompilationStartedAsync(CompilationStartedEvent startedEvent, AnalysisScope analysisScope, AnalysisState analysisStateOpt, CancellationToken cancellationToken) { - ExecuteCompilationActions(_compilationActionsMap, startedEvent, analysisScope, analysisStateOpt, cancellationToken); + return ExecuteCompilationActionsAsync(_compilationActionsMap, startedEvent, analysisScope, analysisStateOpt, cancellationToken); } - private void ProcessCompilationCompleted(CompilationCompletedEvent endEvent, AnalysisScope analysisScope, AnalysisState analysisStateOpt, CancellationToken cancellationToken) + private Task ProcessCompilationCompletedAsync(CompilationCompletedEvent endEvent, AnalysisScope analysisScope, AnalysisState analysisStateOpt, CancellationToken cancellationToken) { - ExecuteCompilationActions(_compilationEndActionsMap, endEvent, analysisScope, analysisStateOpt, cancellationToken); + return ExecuteCompilationActionsAsync(_compilationEndActionsMap, endEvent, analysisScope, analysisStateOpt, cancellationToken); } - private void ExecuteCompilationActions( + private async Task ExecuteCompilationActionsAsync( ImmutableDictionary> compilationActionsMap, CompilationEvent compilationEvent, AnalysisScope analysisScope, @@ -787,11 +790,11 @@ private void ProcessCompilationCompleted(CompilationCompletedEvent endEvent, Ana ImmutableArray compilationActions; if (compilationActionsMap.TryGetValue(analyzer, out compilationActions)) { - analyzerExecutor.ExecuteCompilationActions(compilationActions, analyzer, compilationEvent, analysisScope, analysisStateOpt); + await analyzerExecutor.ExecuteCompilationActionsAsync(compilationActions, analyzer, compilationEvent, analysisScope, analysisStateOpt).ConfigureAwait(false); } - else + else if (analysisStateOpt != null) { - analysisStateOpt?.MarkEventComplete(compilationEvent, analyzer); + await analysisStateOpt.MarkEventCompleteAsync(compilationEvent, analyzer, cancellationToken).ConfigureAwait(false); } } } @@ -988,14 +991,14 @@ internal AnalyzerDriver(ImmutableArray analyzers, Func>.Empty; } builder.Add(analyzerAndActions.Key, actionsByKind); - } + } analyzerActionsByKind = builder.ToImmutable(); } @@ -1116,7 +1119,7 @@ private bool ShouldExecuteOperationBlockActions(AnalysisScope analysisScope, ISy return ShouldExecuteBlockActions(this.OperationBlockStartActionsByAnalyzer, this.OperationBlockActionsByAnalyzer, analysisScope, symbol); } - protected override void ExecuteDeclaringReferenceActions( + protected override async Task ExecuteDeclaringReferenceActionsAsync( SymbolDeclaredCompilationEvent symbolEvent, AnalysisScope analysisScope, AnalysisState analysisStateOpt, @@ -1136,18 +1139,18 @@ private bool ShouldExecuteOperationBlockActions(AnalysisScope analysisScope, ISy if (analysisScope.FilterTreeOpt == null || analysisScope.FilterTreeOpt == decl.SyntaxTree) { - ExecuteDeclaringReferenceActions(decl, symbolEvent, analysisScope, analysisStateOpt, executeSyntaxNodeActions, executeOperationActions, executeCodeBlockActions, executeOperationBlockActions, cancellationToken); + await ExecuteDeclaringReferenceActionsAsync(decl, symbolEvent, analysisScope, analysisStateOpt, executeSyntaxNodeActions, executeOperationActions, executeCodeBlockActions, executeOperationBlockActions, cancellationToken).ConfigureAwait(false); } } } else if (analysisStateOpt != null) { cancellationToken.ThrowIfCancellationRequested(); - analysisStateOpt.MarkDeclarationsComplete(symbolEvent.DeclaringSyntaxReferences, analysisScope.Analyzers); + await analysisStateOpt.MarkDeclarationsCompleteAsync(symbolEvent.DeclaringSyntaxReferences, analysisScope.Analyzers, cancellationToken).ConfigureAwait(false); foreach (var decl in symbolEvent.DeclaringSyntaxReferences) { - ClearCachedAnalysisDataIfAnalyzed(decl, decl.GetSyntax(), symbolEvent.Compilation, analysisStateOpt); + await ClearCachedAnalysisDataIfAnalyzedAsync(decl, decl.GetSyntax(), symbolEvent.Compilation, analysisStateOpt, cancellationToken).ConfigureAwait(false); } } } @@ -1187,13 +1190,13 @@ private bool ShouldExecuteOperationBlockActions(AnalysisScope analysisScope, ISy return data; } - private void ClearCachedAnalysisDataIfAnalyzed(SyntaxReference declaration, SyntaxNode node, Compilation compilation, AnalysisState analysisState) + private async Task ClearCachedAnalysisDataIfAnalyzedAsync(SyntaxReference declaration, SyntaxNode node, Compilation compilation, AnalysisState analysisState, CancellationToken cancellationToken) { Debug.Assert(analysisState != null); CompilationData compilationData; if (!s_compilationDataCache.TryGetValue(compilation, out compilationData) || - !analysisState.IsDeclarationComplete(node)) + !(await analysisState.IsDeclarationCompleteAsync(node, cancellationToken).ConfigureAwait(false))) { return; } @@ -1251,7 +1254,7 @@ private static void ComputeDeclarationsInNode(SemanticModel semanticModel, ISymb semanticModel.ComputeDeclarationsInNode(topmostNodeForAnalysis, getSymbol, builder, cancellationToken, levelsToCompute); } - private void ExecuteDeclaringReferenceActions( + private async Task ExecuteDeclaringReferenceActionsAsync( SyntaxReference decl, SymbolDeclaredCompilationEvent symbolEvent, AnalysisScope analysisScope, @@ -1292,8 +1295,8 @@ private static void ComputeDeclarationsInNode(SemanticModel semanticModel, ISymb ImmutableDictionary>> nodeActionsByKind; if (this.NodeActionsByAnalyzerAndKind.TryGetValue(analyzer, out nodeActionsByKind)) { - analyzerExecutor.ExecuteSyntaxNodeActions(nodesToAnalyze, nodeActionsByKind, - analyzer, semanticModel, _getKind, declarationAnalysisData.TopmostNodeForAnalysis.FullSpan, decl, analysisScope, analysisStateOpt); + await analyzerExecutor.ExecuteSyntaxNodeActionsAsync(nodesToAnalyze, nodeActionsByKind, + analyzer, semanticModel, _getKind, declarationAnalysisData.TopmostNodeForAnalysis.FullSpan, decl, analysisScope, analysisStateOpt).ConfigureAwait(false); } } } @@ -1331,8 +1334,8 @@ private static void ComputeDeclarationsInNode(SemanticModel semanticModel, ISymb ImmutableDictionary> operationActionsByKind; if (this.OperationActionsByAnalyzerAndKind.TryGetValue(analyzer, out operationActionsByKind)) { - analyzerExecutor.ExecuteOperationActions(operationsToAnalyze, operationActionsByKind, - analyzer, semanticModel, declarationAnalysisData.TopmostNodeForAnalysis.FullSpan, decl, analysisScope, analysisStateOpt); + await analyzerExecutor.ExecuteOperationActionsAsync(operationsToAnalyze, operationActionsByKind, + analyzer, semanticModel, declarationAnalysisData.TopmostNodeForAnalysis.FullSpan, decl, analysisScope, analysisStateOpt).ConfigureAwait(false); } } } @@ -1341,28 +1344,28 @@ private static void ComputeDeclarationsInNode(SemanticModel semanticModel, ISymb { foreach (var analyzerActions in codeBlockActions) { - analyzerExecutor.ExecuteOperationBlockActions( + await analyzerExecutor.ExecuteOperationBlockActionsAsync( analyzerActions.OperationBlockStartActions, analyzerActions.OperationBlockActions, analyzerActions.OpererationBlockEndActions, analyzerActions.Analyzer, declarationAnalysisData.TopmostNodeForAnalysis, symbol, - operationBlocksToAnalyze, operationsToAnalyze, semanticModel, decl, analysisScope, analysisStateOpt); + operationBlocksToAnalyze, operationsToAnalyze, semanticModel, decl, analysisScope, analysisStateOpt).ConfigureAwait(false); } } } } - break; + break; + } } } - } if (executableCodeBlocks.Any() && shouldExecuteCodeBlockActions) { foreach (var analyzerActions in codeBlockActions) { - analyzerExecutor.ExecuteCodeBlockActions( + await analyzerExecutor.ExecuteCodeBlockActionsAsync( analyzerActions.CodeBlockStartActions, analyzerActions.CodeBlockActions, analyzerActions.CodeBlockEndActions, analyzerActions.Analyzer, declarationAnalysisData.TopmostNodeForAnalysis, symbol, - executableCodeBlocks, semanticModel, _getKind, decl, analysisScope, analysisStateOpt); + executableCodeBlocks, semanticModel, _getKind, decl, analysisScope, analysisStateOpt).ConfigureAwait(false); } } } @@ -1372,12 +1375,12 @@ private static void ComputeDeclarationsInNode(SemanticModel semanticModel, ISymb { foreach (var analyzer in analysisScope.Analyzers) { - analysisStateOpt.MarkDeclarationComplete(decl, analyzer); + await analysisStateOpt.MarkDeclarationCompleteAsync(decl, analyzer, cancellationToken).ConfigureAwait(false); } if (cacheAnalysisData) { - ClearCachedAnalysisDataIfAnalyzed(decl, declarationAnalysisData.DeclaringReferenceSyntax, symbolEvent.Compilation, analysisStateOpt); + await ClearCachedAnalysisDataIfAnalyzedAsync(decl, declarationAnalysisData.DeclaringReferenceSyntax, symbolEvent.Compilation, analysisStateOpt, cancellationToken).ConfigureAwait(false); } } } diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerExecutor.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerExecutor.cs index e11512c1096..42ec841b265 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerExecutor.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerExecutor.cs @@ -15,9 +15,8 @@ using AnalyzerStateData = Microsoft.CodeAnalysis.Diagnostics.AnalysisState.AnalyzerStateData; using SyntaxNodeAnalyzerStateData = Microsoft.CodeAnalysis.Diagnostics.AnalysisState.SyntaxNodeAnalyzerStateData; using OperationAnalyzerStateData = Microsoft.CodeAnalysis.Diagnostics.AnalysisState.OperationAnalyzerStateData; -using CodeBlockAnalyzerStateData = Microsoft.CodeAnalysis.Diagnostics.AnalysisState.CodeBlockAnalyzerStateData; using DeclarationAnalyzerStateData = Microsoft.CodeAnalysis.Diagnostics.AnalysisState.DeclarationAnalyzerStateData; - +using System.Threading.Tasks; namespace Microsoft.CodeAnalysis.Diagnostics { @@ -40,7 +39,7 @@ internal class AnalyzerExecutor private readonly Action _onAnalyzerException; private readonly AnalyzerManager _analyzerManager; private readonly Func _isCompilerAnalyzer; - private readonly Func _getAnalyzerGateOpt; + private readonly Func _getAnalyzerGateOpt; private readonly ConcurrentDictionary _analyzerExecutionTimeMapOpt; private readonly CancellationToken _cancellationToken; @@ -74,7 +73,7 @@ internal class AnalyzerExecutor Action onAnalyzerException, Func isCompilerAnalyzer, AnalyzerManager analyzerManager, - Func getAnalyzerGate, + Func getAnalyzerGate, bool logExecutionTime = false, Action addLocalDiagnosticOpt = null, Action addNonLocalDiagnosticOpt = null, @@ -123,7 +122,7 @@ internal class AnalyzerExecutor Action onAnalyzerException, Func isCompilerAnalyzer, AnalyzerManager analyzerManager, - Func getAnalyzerGateOpt, + Func getAnalyzerGateOpt, ConcurrentDictionary analyzerExecutionTimeMapOpt, Action addLocalDiagnosticOpt, Action addNonLocalDiagnosticOpt, @@ -166,13 +165,13 @@ public AnalyzerExecutor WithCancellationToken(CancellationToken cancellationToke /// Session scope to store register session wide analyzer actions. /// /// Note that this API doesn't execute any registered by the Initialize invocation. - /// Use API + /// Use API /// to get execute these actions to get the per-compilation analyzer actions. /// - public void ExecuteInitializeMethod(DiagnosticAnalyzer analyzer, HostSessionStartAnalysisScope sessionScope) + public async Task ExecuteInitializeMethodAsync(DiagnosticAnalyzer analyzer, HostSessionStartAnalysisScope sessionScope) { // The Initialize method should be run asynchronously in case it is not well behaved, e.g. does not terminate. - ExecuteAndCatchIfThrows(analyzer, () => analyzer.Initialize(new AnalyzerAnalysisContext(analyzer, sessionScope))); + await ExecuteAndCatchIfThrowsAsync(analyzer, () => analyzer.Initialize(new AnalyzerAnalysisContext(analyzer, sessionScope))).ConfigureAwait(false); } /// @@ -180,13 +179,13 @@ public void ExecuteInitializeMethod(DiagnosticAnalyzer analyzer, HostSessionStar /// /// whose compilation start actions are to be executed. /// Compilation scope to store the analyzer actions. - public void ExecuteCompilationStartActions(ImmutableArray actions, HostCompilationStartAnalysisScope compilationScope) + public async Task ExecuteCompilationStartActionsAsync(ImmutableArray actions, HostCompilationStartAnalysisScope compilationScope) { foreach (var startAction in actions) { _cancellationToken.ThrowIfCancellationRequested(); - ExecuteAndCatchIfThrows(startAction.Analyzer, - () => startAction.Action(new AnalyzerCompilationStartAnalysisContext(startAction.Analyzer, compilationScope, _compilation, _analyzerOptions, _cancellationToken))); + await ExecuteAndCatchIfThrowsAsync(startAction.Analyzer, + () => startAction.Action(new AnalyzerCompilationStartAnalysisContext(startAction.Analyzer, compilationScope, _compilation, _analyzerOptions, _cancellationToken))).ConfigureAwait(false); } } @@ -198,7 +197,7 @@ public void ExecuteCompilationStartActions(ImmutableArrayCompilation event. /// Scope for analyzer execution. /// An optional object to track analysis state. - public void ExecuteCompilationActions( + public async Task ExecuteCompilationActionsAsync( ImmutableArray compilationActions, DiagnosticAnalyzer analyzer, CompilationEvent compilationEvent, @@ -211,10 +210,15 @@ public void ExecuteCompilationStartActions(ImmutableArray compilationActions, DiagnosticAnalyzer analyzer, AnalyzerStateData analyzerStateOpt) + private async Task ExecuteCompilationActionsCoreAsync(ImmutableArray compilationActions, DiagnosticAnalyzer analyzer, AnalyzerStateData analyzerStateOpt) { var addDiagnostic = GetAddCompilationDiagnostic(analyzer); @@ -233,9 +237,9 @@ private void ExecuteCompilationActionsCore(ImmutableArray endAction.Action(new CompilationAnalysisContext(_compilation, _analyzerOptions, addDiagnostic, - d => IsSupportedDiagnostic(endAction.Analyzer, d), _cancellationToken))); + d => IsSupportedDiagnostic(endAction.Analyzer, d), _cancellationToken))).ConfigureAwait(false); analyzerStateOpt?.ProcessedActions.Add(endAction); } @@ -251,7 +255,7 @@ private void ExecuteCompilationActionsCore(ImmutableArrayDelegate to get topmost declaration node for a symbol declaration reference. /// Scope for analyzer execution. /// An optional object to track analysis state. - public void ExecuteSymbolActions( + public async Task ExecuteSymbolActionsAsync( ImmutableArray symbolActions, DiagnosticAnalyzer analyzer, ISymbol symbol, @@ -263,10 +267,15 @@ private void ExecuteCompilationActionsCore(ImmutableArray symbolActions, DiagnosticAnalyzer analyzer, ISymbol symbol, @@ -297,9 +306,9 @@ private void ExecuteCompilationActionsCore(ImmutableArray action(new SymbolAnalysisContext(symbol, _compilation, _analyzerOptions, addDiagnostic, - d => IsSupportedDiagnostic(symbolAction.Analyzer, d), _cancellationToken))); + d => IsSupportedDiagnostic(symbolAction.Analyzer, d), _cancellationToken))).ConfigureAwait(false); analyzerStateOpt?.ProcessedActions.Add(symbolAction); } @@ -316,7 +325,7 @@ private void ExecuteCompilationActionsCore(ImmutableArrayCompilation event for semantic model analysis. /// Scope for analyzer execution. /// An optional object to track analysis state. - public void ExecuteSemanticModelActions( + public async Task ExecuteSemanticModelActionsAsync( ImmutableArray semanticModelActions, DiagnosticAnalyzer analyzer, SemanticModel semanticModel, @@ -328,10 +337,15 @@ private void ExecuteCompilationActionsCore(ImmutableArray semanticModelActions, DiagnosticAnalyzer analyzer, SemanticModel semanticModel, @@ -355,9 +369,9 @@ private void ExecuteCompilationActionsCore(ImmutableArray semanticModelAction.Action(new SemanticModelAnalysisContext(semanticModel, _analyzerOptions, addDiagnostic, - d => IsSupportedDiagnostic(semanticModelAction.Analyzer, d), _cancellationToken))); + d => IsSupportedDiagnostic(semanticModelAction.Analyzer, d), _cancellationToken))).ConfigureAwait(false); analyzerStateOpt?.ProcessedActions.Add(semanticModelAction); } @@ -372,7 +386,7 @@ private void ExecuteCompilationActionsCore(ImmutableArraySyntax tree to analyze. /// Scope for analyzer execution. /// An optional object to track analysis state. - public void ExecuteSyntaxTreeActions( + public async Task ExecuteSyntaxTreeActionsAsync( ImmutableArray syntaxTreeActions, DiagnosticAnalyzer analyzer, SyntaxTree tree, @@ -383,10 +397,15 @@ private void ExecuteCompilationActionsCore(ImmutableArray syntaxTreeActions, DiagnosticAnalyzer analyzer, SyntaxTree tree, @@ -410,16 +429,16 @@ private void ExecuteCompilationActionsCore(ImmutableArray syntaxTreeAction.Action(new SyntaxTreeAnalysisContext(tree, _analyzerOptions, addDiagnostic, - d => IsSupportedDiagnostic(syntaxTreeAction.Analyzer, d), _cancellationToken))); + d => IsSupportedDiagnostic(syntaxTreeAction.Analyzer, d), _cancellationToken))).ConfigureAwait(false); analyzerStateOpt?.ProcessedActions.Add(syntaxTreeAction); } } } - private void ExecuteSyntaxNodeAction( + private async Task ExecuteSyntaxNodeActionAsync( SyntaxNodeAnalyzerAction syntaxNodeAction, SyntaxNode node, SemanticModel semanticModel, @@ -433,13 +452,13 @@ private void ExecuteCompilationActionsCore(ImmutableArray IsSupportedDiagnostic(syntaxNodeAction.Analyzer, d), _cancellationToken); - ExecuteAndCatchIfThrows(syntaxNodeAction.Analyzer, () => syntaxNodeAction.Action(syntaxNodeContext)); + await ExecuteAndCatchIfThrowsAsync(syntaxNodeAction.Analyzer, () => syntaxNodeAction.Action(syntaxNodeContext)).ConfigureAwait(false); analyzerStateOpt?.ProcessedActions.Add(syntaxNodeAction); } } - private void ExecuteOperationAction( + private async Task ExecuteOperationActionAsync( OperationAnalyzerAction operationAction, IOperation operation, Action addDiagnostic, @@ -450,13 +469,13 @@ private void ExecuteCompilationActionsCore(ImmutableArray IsSupportedDiagnostic(operationAction.Analyzer, d), _cancellationToken); - ExecuteAndCatchIfThrows(operationAction.Analyzer, () => operationAction.Action(operationContext)); + await ExecuteAndCatchIfThrowsAsync(operationAction.Analyzer, () => operationAction.Action(operationContext)).ConfigureAwait(false); analyzerStateOpt?.ProcessedActions.Add(operationAction); } } - public void ExecuteCodeBlockActions( + public async Task ExecuteCodeBlockActionsAsync( IEnumerable> codeBlockStartActions, IEnumerable codeBlockActions, IEnumerable codeBlockEndActions, @@ -475,11 +494,12 @@ private void ExecuteCompilationActionsCore(ImmutableArray, CodeBlockAnalyzerAction, SyntaxNodeAnalyzerAction, SyntaxNodeAnalyzerStateData, SyntaxNode, TLanguageKindEnum>( + await ExecuteBlockActionsCoreAsync, CodeBlockAnalyzerAction, SyntaxNodeAnalyzerAction, SyntaxNodeAnalyzerStateData, SyntaxNode, TLanguageKindEnum>( codeBlockStartActions, codeBlockActions, codeBlockEndActions, analyzer, - declaredNode, declaredSymbol, executableCodeBlocks, (codeBlocks) => codeBlocks.SelectMany(cb => cb.DescendantNodesAndSelf()), semanticModel, getKind, analyzerStateOpt?.CodeBlockAnalysisState); + declaredNode, declaredSymbol, executableCodeBlocks, (codeBlocks) => codeBlocks.SelectMany(cb => cb.DescendantNodesAndSelf()), semanticModel, getKind, analyzerStateOpt?.CodeBlockAnalysisState).ConfigureAwait(false); } } finally @@ -488,7 +508,7 @@ private void ExecuteCompilationActionsCore(ImmutableArray operationBlockStartActions, IEnumerable operationBlockActions, IEnumerable operationBlockEndActions, @@ -506,11 +526,12 @@ private void ExecuteCompilationActionsCore(ImmutableArray( + await ExecuteBlockActionsCoreAsync( operationBlockStartActions, operationBlockActions, operationBlockEndActions, analyzer, - declaredNode, declaredSymbol, operationBlocks, (blocks) => operations, semanticModel, null, analyzerStateOpt?.OperationBlockAnalysisState); + declaredNode, declaredSymbol, operationBlocks, (blocks) => operations, semanticModel, null, analyzerStateOpt?.OperationBlockAnalysisState).ConfigureAwait(false); } } finally @@ -519,7 +540,7 @@ private void ExecuteCompilationActionsCore(ImmutableArray( + private async Task ExecuteBlockActionsCoreAsync( IEnumerable startActions, IEnumerable actions, IEnumerable endActions, @@ -582,7 +603,7 @@ private void ExecuteCompilationActionsCore(ImmutableArray; // Catch Exception from the start action. - ExecuteAndCatchIfThrows(startAction.Analyzer, () => + await ExecuteAndCatchIfThrowsAsync(startAction.Analyzer, () => { var codeBlockScope = new HostCodeBlockStartAnalysisScope(); var blockStartContext = new AnalyzerCodeBlockStartAnalysisContext(startAction.Analyzer, @@ -590,7 +611,7 @@ private void ExecuteCompilationActionsCore(ImmutableArray; // Catch Exception from the start action. - ExecuteAndCatchIfThrows(startAction.Analyzer, () => + await ExecuteAndCatchIfThrowsAsync(startAction.Analyzer, () => { var operationBlockScope = new HostOperationBlockStartAnalysisScope(); var operationStartContext = new AnalyzerOperationBlockStartAnalysisContext(startAction.Analyzer, @@ -607,7 +628,7 @@ private void ExecuteCompilationActionsCore(ImmutableArray)getNodesToAnalyze(executableBlocks); - ExecuteSyntaxNodeActions(syntaxNodesToAnalyze, executableNodeActionsByKind, semanticModel, getKind, addDiagnostic, analyzerStateOpt?.ExecutableNodesAnalysisState as SyntaxNodeAnalyzerStateData); + await ExecuteSyntaxNodeActionsAsync(syntaxNodesToAnalyze, executableNodeActionsByKind, semanticModel, getKind, addDiagnostic, analyzerStateOpt?.ExecutableNodesAnalysisState as SyntaxNodeAnalyzerStateData).ConfigureAwait(false); } else if (operationActions != null) { var operationActionsByKind = GetOperationActionsByKind(operationActions); var operationsToAnalyze = (IEnumerable)getNodesToAnalyze(executableBlocks); - ExecuteOperationActions(operationsToAnalyze, operationActionsByKind, addDiagnostic, analyzerStateOpt?.ExecutableNodesAnalysisState as OperationAnalyzerStateData); + await ExecuteOperationActionsAsync(operationsToAnalyze, operationActionsByKind, addDiagnostic, analyzerStateOpt?.ExecutableNodesAnalysisState as OperationAnalyzerStateData).ConfigureAwait(false); } } executableNodeActions.Free(); - ExecuteBlockActions(blockActions, declaredNode, declaredSymbol, semanticModel, operationBlocks, addDiagnostic, analyzerStateOpt); - ExecuteBlockActions(blockEndActions, declaredNode, declaredSymbol, semanticModel, operationBlocks, addDiagnostic, analyzerStateOpt); + await ExecuteBlockActionsAsync(blockActions, declaredNode, declaredSymbol, semanticModel, operationBlocks, addDiagnostic, analyzerStateOpt).ConfigureAwait(false); + await ExecuteBlockActionsAsync(blockEndActions, declaredNode, declaredSymbol, semanticModel, operationBlocks, addDiagnostic, analyzerStateOpt).ConfigureAwait(false); } - private void ExecuteBlockActions( + private async Task ExecuteBlockActionsAsync( PooledHashSet blockActions, SyntaxNode declaredNode, ISymbol declaredSymbol, @@ -666,18 +687,18 @@ private void ExecuteCompilationActionsCore(ImmutableArray isSupportedDiagnostic = d => IsSupportedDiagnostic(blockAction.Analyzer, d); if (codeBlockAction != null) { - ExecuteAndCatchIfThrows( + await ExecuteAndCatchIfThrowsAsync( codeBlockAction.Analyzer, - () => codeBlockAction.Action(new CodeBlockAnalysisContext(declaredNode, declaredSymbol, semanticModel, _analyzerOptions, addDiagnostic, isSupportedDiagnostic, _cancellationToken))); + () => codeBlockAction.Action(new CodeBlockAnalysisContext(declaredNode, declaredSymbol, semanticModel, _analyzerOptions, addDiagnostic, isSupportedDiagnostic, _cancellationToken))).ConfigureAwait(false); } else { var operationBlockAction = blockAction as OperationBlockAnalyzerAction; if (operationBlockAction != null) { - ExecuteAndCatchIfThrows( + await ExecuteAndCatchIfThrowsAsync( operationBlockAction.Analyzer, - () => operationBlockAction.Action(new OperationBlockAnalysisContext(operationBlocks, declaredSymbol, _analyzerOptions, addDiagnostic, isSupportedDiagnostic, _cancellationToken))); + () => operationBlockAction.Action(new OperationBlockAnalysisContext(operationBlocks, declaredSymbol, _analyzerOptions, addDiagnostic, isSupportedDiagnostic, _cancellationToken))).ConfigureAwait(false); } } @@ -715,7 +736,7 @@ private void ExecuteCompilationActionsCore(ImmutableArray( + public async Task ExecuteSyntaxNodeActionsAsync( IEnumerable nodesToAnalyze, IDictionary>> nodeActionsByKind, DiagnosticAnalyzer analyzer, @@ -731,9 +752,10 @@ private void ExecuteCompilationActionsCore(ImmutableArray( + private async Task ExecuteSyntaxNodeActionsCoreAsync( IEnumerable nodesToAnalyze, IDictionary>> nodeActionsByKind, DiagnosticAnalyzer analyzer, @@ -753,10 +775,10 @@ private void ExecuteCompilationActionsCore(ImmutableArray( + private async Task ExecuteSyntaxNodeActionsAsync( IEnumerable nodesToAnalyze, IDictionary>> nodeActionsByKind, SemanticModel model, @@ -771,7 +793,7 @@ private void ExecuteCompilationActionsCore(ImmutableArray( + private async Task ExecuteSyntaxNodeActionsAsync( SyntaxNode node, IDictionary>> nodeActionsByKind, SemanticModel model, @@ -799,7 +821,7 @@ private void ExecuteCompilationActionsCore(ImmutableArray operationsToAnalyze, IDictionary> operationActionsByKind, DiagnosticAnalyzer analyzer, @@ -845,9 +867,10 @@ private void ExecuteCompilationActionsCore(ImmutableArray operationsToAnalyze, IDictionary> operationActionsByKind, DiagnosticAnalyzer analyzer, @@ -865,10 +888,10 @@ private void ExecuteCompilationActionsCore(ImmutableArray operationsToAnalyze, IDictionary> operationActionsByKind, Action addDiagnostic, @@ -880,7 +903,7 @@ private void ExecuteCompilationActionsCore(ImmutableArray> operationActionsByKind, Action addDiagnostic, @@ -905,7 +928,7 @@ private void ExecuteCompilationActionsCore(ImmutableArray TryStartProcessingEventAsync(CompilationEvent nonSymbolCompilationEvent, DiagnosticAnalyzer analyzer, AnalysisScope analysisScope, AnalysisState analysisStateOpt, CancellationToken cancellationToken) { Debug.Assert(!(nonSymbolCompilationEvent is SymbolDeclaredCompilationEvent)); Debug.Assert(analysisScope.Analyzers.Contains(analyzer)); - analyzerStateOpt = null; - return analysisStateOpt == null || analysisStateOpt.TryStartProcessingEvent(nonSymbolCompilationEvent, analyzer, out analyzerStateOpt); + if (analysisStateOpt == null) + { + return null; + } + + return await analysisStateOpt.TryStartProcessingEventAsync(nonSymbolCompilationEvent, analyzer, cancellationToken).ConfigureAwait(false); } - private static bool TryStartSyntaxAnalysis(SyntaxTree tree, DiagnosticAnalyzer analyzer, AnalysisScope analysisScope, AnalysisState analysisStateOpt, out AnalyzerStateData analyzerStateOpt) + private static async Task TryStartSyntaxAnalysis(SyntaxTree tree, DiagnosticAnalyzer analyzer, AnalysisScope analysisScope, AnalysisState analysisStateOpt, CancellationToken cancellationToken) { Debug.Assert(analysisScope.Analyzers.Contains(analyzer)); - analyzerStateOpt = null; - return analysisStateOpt == null || analysisStateOpt.TryStartSyntaxAnalysis(tree, analyzer, out analyzerStateOpt); + if (analysisStateOpt == null) + { + return null; + } + + return await analysisStateOpt.TryStartSyntaxAnalysisAsync(tree, analyzer, cancellationToken).ConfigureAwait(false); } - private static bool TryStartAnalyzingSymbol(ISymbol symbol, DiagnosticAnalyzer analyzer, AnalysisScope analysisScope, AnalysisState analysisStateOpt, out AnalyzerStateData analyzerStateOpt) + private static async Task TryStartAnalyzingSymbolAsync(ISymbol symbol, DiagnosticAnalyzer analyzer, AnalysisScope analysisScope, AnalysisState analysisStateOpt, CancellationToken cancellationToken) { Debug.Assert(analysisScope.Analyzers.Contains(analyzer)); - analyzerStateOpt = null; - return analysisStateOpt == null || analysisStateOpt.TryStartAnalyzingSymbol(symbol, analyzer, out analyzerStateOpt); + if (analysisStateOpt == null) + { + return null; + } + + return await analysisStateOpt.TryStartAnalyzingSymbolAsync(symbol, analyzer, cancellationToken).ConfigureAwait(false); } - private static bool TryStartAnalyzingSyntaxRefence(SyntaxReference syntaxReference, DiagnosticAnalyzer analyzer, AnalysisScope analysisScope, AnalysisState analysisStateOpt, out DeclarationAnalyzerStateData analyzerStateOpt) + private static async Task TryStartAnalyzingSyntaxReferenceAsync(SyntaxReference syntaxReference, DiagnosticAnalyzer analyzer, AnalysisScope analysisScope, AnalysisState analysisStateOpt, CancellationToken cancellationToken) { Debug.Assert(analysisScope.Analyzers.Contains(analyzer)); - analyzerStateOpt = null; - return analysisStateOpt == null || analysisStateOpt.TryStartAnalyzingDeclaration(syntaxReference, analyzer, out analyzerStateOpt); + if (analysisStateOpt == null) + { + return null; + } + + return await analysisStateOpt.TryStartAnalyzingDeclarationAsync(syntaxReference, analyzer, cancellationToken).ConfigureAwait(false); } - private static bool TryStartAnalyzingOperationReference(SyntaxReference syntaxReference, DiagnosticAnalyzer analyzer, AnalysisScope analysisScope, AnalysisState analysisStateOpt, out OperationAnalyzerStateData analyzerStateOpt) + private static async Task TryStartAnalyzingOperationReferenceAsync(SyntaxReference syntaxReference, DiagnosticAnalyzer analyzer, AnalysisScope analysisScope, AnalysisState analysisStateOpt, CancellationToken cancellationToken) { Debug.Assert(analysisScope.Analyzers.Contains(analyzer)); - analyzerStateOpt = null; - DeclarationAnalyzerStateData declarationAnalyzerStateOpt; if (analysisStateOpt == null) { - return true; - } - - if (analysisStateOpt.TryStartAnalyzingDeclaration(syntaxReference, analyzer, out declarationAnalyzerStateOpt)) - { - analyzerStateOpt = declarationAnalyzerStateOpt.OperationBlockAnalysisState.ExecutableNodesAnalysisState; - return true; + return null; } - analyzerStateOpt = null; - return false; + var declarationAnalyzerStateOpt = await analysisStateOpt.TryStartAnalyzingDeclarationAsync(syntaxReference, analyzer, cancellationToken).ConfigureAwait(false); + return declarationAnalyzerStateOpt?.OperationBlockAnalysisState.ExecutableNodesAnalysisState; } internal TimeSpan ResetAnalyzerExecutionTime(DiagnosticAnalyzer analyzer) diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerManager.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerManager.cs index 91be939f37d..f103d24ed84 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerManager.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerManager.cs @@ -55,10 +55,10 @@ internal partial class AnalyzerManager { Func> 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> 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 GetAnalyzerActionsAsync(DiagnosticAnalyzer an var supportedDiagnostics = ImmutableArray.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 handler = null; Action onAnalyzerException = analyzerExecutor.OnAnalyzerException; diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/CompilationWithAnalyzers.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/CompilationWithAnalyzers.cs index 3dd176b5959..20eb8912f11 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/CompilationWithAnalyzers.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/CompilationWithAnalyzers.cs @@ -68,7 +68,7 @@ public class CompilationWithAnalyzers /// Pool of event queues to serve each diagnostics request. /// private static readonly ObjectPool> s_eventQueuePool = new ObjectPool>(() => new AsyncQueue()); - private static readonly AsyncQueue s_EmptyEventQueue = new AsyncQueue(); + private static readonly Task> s_EmptyEventQueueTask = Task.FromResult(new AsyncQueue()); /// @@ -362,11 +362,11 @@ private async Task> GetAnalyzerDiagnosticsCoreAsync(I } }; - Func> getEventQueue = () => - GetPendingEvents(analyzers, includeSourceEvents, includeNonSourceEvents); + Func>> 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> GetAnalyzerSyntaxDiagnosticsCoreA var analysisScope = new AnalysisScope(analyzers, tree, filterSpan: null, syntaxAnalysis: true, concurrentAnalysis: _analysisOptions.ConcurrentAnalysis, categorizeDiagnostics: true); Action generateCompilationEvents = null; - Func> getEventQueue = () => s_EmptyEventQueue; + Func>> 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> GetAnalyzerSemanticDiagnosticsCor Action generateCompilationEvents = () => AnalyzerDriver.GetOrCreateCachedSemanticModel(model.SyntaxTree, _compilation, cancellationToken); - Func> getEventQueue = () => GetPendingEvents(analyzers, model.SyntaxTree); + Func>> 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> GetAnalyzerSemanticDiagnosticsCor } } - private async Task ComputeAnalyzerDiagnosticsAsync(AnalysisScope analysisScope, Action generateCompilationEventsOpt, Func> getEventQueue, int newTaskToken, CancellationToken cancellationToken) + private async Task ComputeAnalyzerDiagnosticsAsync(AnalysisScope analysisScope, Action generateCompilationEventsOpt, Func>> getEventQueueTask, int newTaskToken, CancellationToken cancellationToken) { try { @@ -552,11 +553,11 @@ private async Task ComputeAnalyzerDiagnosticsAsync(AnalysisScope analysisScope, AsyncQueue 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 DequeueGeneratedCompilationEvents() return builder.ToImmutable(); } - private AsyncQueue GetPendingEvents(ImmutableArray analyzers, SyntaxTree tree) + private async Task> GetPendingEventsAsync(ImmutableArray 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 GetPendingEvents(ImmutableArray GetPendingEvents(ImmutableArray analyzers, bool includeSourceEvents, bool includeNonSourceEvents) + private async Task> GetPendingEventsAsync(ImmutableArray analyzers, bool includeSourceEvents, bool includeNonSourceEvents, CancellationToken cancellationToken) { Debug.Assert(includeSourceEvents || includeNonSourceEvents); @@ -913,7 +915,8 @@ private AsyncQueue GetPendingEvents(ImmutableArray GetPendingEvents(ImmutableArray eventQueue) { - if (eventQueue == null || ReferenceEquals(eventQueue, s_EmptyEventQueue)) + if (eventQueue == null || ReferenceEquals(eventQueue, s_EmptyEventQueueTask.Result)) { return; } diff --git a/src/Workspaces/Core/Portable/Utilities/SemaphoreSlimExtensions.cs b/src/Compilers/Core/Portable/InternalUtilities/SemaphoreSlimExtensions.cs similarity index 100% rename from src/Workspaces/Core/Portable/Utilities/SemaphoreSlimExtensions.cs rename to src/Compilers/Core/Portable/InternalUtilities/SemaphoreSlimExtensions.cs diff --git a/src/Workspaces/Core/Portable/Utilities/SemaphoreSlimFactory.cs b/src/Compilers/Core/Portable/InternalUtilities/SemaphoreSlimFactory.cs similarity index 100% rename from src/Workspaces/Core/Portable/Utilities/SemaphoreSlimFactory.cs rename to src/Compilers/Core/Portable/InternalUtilities/SemaphoreSlimFactory.cs diff --git a/src/Workspaces/Core/Portable/Workspaces.csproj b/src/Workspaces/Core/Portable/Workspaces.csproj index 5694f2e52a6..441993d27dd 100644 --- a/src/Workspaces/Core/Portable/Workspaces.csproj +++ b/src/Workspaces/Core/Portable/Workspaces.csproj @@ -118,6 +118,12 @@ InternalUtilities\ReferenceEqualityComparer.cs + + InternalUtilities\SemaphoreSlimExtensions.cs + + + InternalUtilities\SemaphoreSlimFactory.cs + InternalUtilities\SpecializedCollections.cs @@ -787,7 +793,6 @@ - @@ -806,7 +811,6 @@ - -- GitLab