diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalysisState.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalysisState.cs index c451c9c929f36bf76d6424992bbda5e8a190a779..61a39b3cb2103a6da773bae7db0fdbefb69200e9 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalysisState.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalysisState.cs @@ -20,7 +20,7 @@ namespace Microsoft.CodeAnalysis.Diagnostics /// internal partial class AnalysisState { - private readonly object _gate; + private readonly SemaphoreSlim _gate; /// /// Per-analyzer analysis state map. @@ -61,7 +61,7 @@ internal partial class AnalysisState public AnalysisState(ImmutableArray analyzers, CompilationData compilationData, CompilationOptions compilationOptions) { - _gate = new object(); + _gate = new SemaphoreSlim(initialCount: 1); _analyzerStateMap = CreateAnalyzerStateMap(analyzers, out _analyzerStates); _compilationData = compilationData; _compilationOptions = compilationOptions; @@ -135,7 +135,7 @@ private PerAnalyzerState GetAnalyzerState(DiagnosticAnalyzer analyzer) AnalyzerDriver driver, CancellationToken cancellationToken) { - lock (_gate) + using (_gate.DisposableWait(cancellationToken)) { if (_treesWithGeneratedSourceEvents.Contains(tree)) { @@ -147,7 +147,7 @@ private PerAnalyzerState GetAnalyzerState(DiagnosticAnalyzer analyzer) var symbols = GetDeclaredSymbolsInTree(tree, compilation, getCachedSemanticModel, cancellationToken); var compilationEvents = CreateCompilationEventsForTree(symbols.Concat(globalNs), tree, compilation); - lock (_gate) + using (_gate.DisposableWait(cancellationToken)) { if (_treesWithGeneratedSourceEvents.Contains(tree)) { @@ -191,7 +191,7 @@ private static ImmutableArray CreateCompilationEventsForTree(I private void GenerateSimulatedCompilationNonSourceEvent(Compilation compilation, AnalyzerDriver driver, bool started, CancellationToken cancellationToken) { - lock (_gate) + using (_gate.DisposableWait(cancellationToken)) { var eventAlreadyGenerated = started ? _compilationStartGenerated : _compilationEndGenerated; if (eventAlreadyGenerated) @@ -218,7 +218,7 @@ public async Task OnCompilationEventsGeneratedAsync(ImmutableArray /// Gets pending events for given set of analyzers for the given syntax tree. /// - public ImmutableArray GetPendingEvents(ImmutableArray analyzers, SyntaxTree tree) + public ImmutableArray GetPendingEvents(ImmutableArray analyzers, SyntaxTree tree, CancellationToken cancellationToken) { - lock (_gate) + using (_gate.DisposableWait(cancellationToken)) { return GetPendingEvents_NoLock(analyzers, tree); } @@ -544,9 +545,14 @@ 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 ImmutableArray GetPendingEvents( + ImmutableArray analyzers, + bool includeSourceEvents, + bool includeNonSourceEvents, + CancellationToken cancellationToken) { - lock (_gate) + using (_gate.DisposableWait(cancellationToken)) { return GetPendingEvents_NoLock(analyzers, includeSourceEvents, includeNonSourceEvents); } @@ -628,11 +634,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 bool HasPendingSymbolAnalysis(AnalysisScope analysisScope, CancellationToken cancellationToken) { Debug.Assert(analysisScope.FilterTreeOpt != null); - var symbolDeclaredEvents = GetPendingSymbolDeclaredEvents(analysisScope.FilterTreeOpt); + var symbolDeclaredEvents = GetPendingSymbolDeclaredEvents(analysisScope.FilterTreeOpt, cancellationToken); foreach (var symbolDeclaredEvent in symbolDeclaredEvents) { if (analysisScope.ShouldAnalyze(symbolDeclaredEvent.Symbol)) @@ -651,11 +657,11 @@ public bool HasPendingSymbolAnalysis(AnalysisScope analysisScope) return false; } - private ImmutableArray GetPendingSymbolDeclaredEvents(SyntaxTree tree) + private ImmutableArray GetPendingSymbolDeclaredEvents(SyntaxTree tree, CancellationToken cancellationToken) { Debug.Assert(tree != null); - lock (_gate) + using (_gate.DisposableWait(cancellationToken)) { HashSet compilationEvents; if (!_pendingSourceEvents.TryGetValue(tree, out compilationEvents)) diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/CompilationWithAnalyzers.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/CompilationWithAnalyzers.cs index 044b30e837c1dcc39969f5360e640cd7bc4c190c..ac74d0e36231fb634a50383624d5156baa99b7ed 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/CompilationWithAnalyzers.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/CompilationWithAnalyzers.cs @@ -355,7 +355,7 @@ private async Task> GetAnalyzerCompilationDiagnostics var diagnostics = ImmutableArray.Empty; var analysisScope = new AnalysisScope(_compilation, analyzers, _analysisOptions.ConcurrentAnalysis, categorizeDiagnostics: true); Func> getEventQueue = () => - GetPendingEvents(analyzers, includeSourceEvents: true, includeNonSourceEvents: true); + GetPendingEvents(analyzers, includeSourceEvents: true, includeNonSourceEvents: true, cancellationToken); // Compute the analyzer diagnostics for the given analysis scope. await ComputeAnalyzerDiagnosticsAsync(analysisScope, getEventQueue, newTaskToken: 0, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -580,18 +580,20 @@ private async Task> GetAnalyzerSemanticDiagnosticsCor { var pendingAnalysisScope = pendingAnalyzers.Length < analyzers.Length ? analysisScope.WithAnalyzers(pendingAnalyzers) : analysisScope; - Func> getEventQueue = () => GetPendingEvents(analyzers, model.SyntaxTree); + Func> getEventQueue = () => GetPendingEvents(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 { + cancellationToken.ThrowIfCancellationRequested(); + (ImmutableArray compilationEvents, bool hasSymbolStartActions) = await ComputeAnalyzerDiagnosticsAsync(pendingAnalysisScope, getEventQueue, taskToken, cancellationToken).ConfigureAwait(false); if (hasSymbolStartActions) { await processPartialSymbolLocationsAsync(compilationEvents, analysisScope).ConfigureAwait(false); } - } while (_analysisOptions.ConcurrentAnalysis && _analysisState.HasPendingSymbolAnalysis(pendingAnalysisScope)); + } while (_analysisOptions.ConcurrentAnalysis && _analysisState.HasPendingSymbolAnalysis(pendingAnalysisScope, cancellationToken)); if (_analysisOptions.ConcurrentAnalysis) { @@ -642,12 +644,24 @@ async Task processPartialSymbolLocationsAsync(ImmutableArray c if (partialTrees != null) { - await Task.WhenAll(partialTrees.Select(t => - Task.Run(() => + if (AnalysisOptions.ConcurrentAnalysis) + { + await Task.WhenAll(partialTrees.Select(tree => + Task.Run(() => + { + var treeModel = _compilationData.GetOrCreateCachedSemanticModel(tree, _compilation, cancellationToken); + return GetAnalyzerSemanticDiagnosticsAsync(treeModel, filterSpan: null, cancellationToken); + }, cancellationToken))).ConfigureAwait(false); + } + else + { + foreach (var tree in partialTrees) { - var treeModel = _compilationData.GetOrCreateCachedSemanticModel(t, _compilation, cancellationToken); - return GetAnalyzerSemanticDiagnosticsAsync(treeModel, filterSpan: null, cancellationToken); - }))).ConfigureAwait(false); + cancellationToken.ThrowIfCancellationRequested(); + var treeModel = _compilationData.GetOrCreateCachedSemanticModel(tree, _compilation, cancellationToken); + await GetAnalyzerSemanticDiagnosticsAsync(treeModel, filterSpan: null, cancellationToken).ConfigureAwait(false); + } + } } } } @@ -1062,13 +1076,13 @@ private void ClearExecutingTask(Task computeTask, SyntaxTree treeOpt) } } - private AsyncQueue GetPendingEvents(ImmutableArray analyzers, SyntaxTree tree) + private AsyncQueue GetPendingEvents(ImmutableArray analyzers, SyntaxTree tree, CancellationToken cancellationToken) { var eventQueue = _eventQueuePool.Allocate(); Debug.Assert(!eventQueue.IsCompleted); Debug.Assert(eventQueue.Count == 0); - foreach (var compilationEvent in _analysisState.GetPendingEvents(analyzers, tree)) + foreach (var compilationEvent in _analysisState.GetPendingEvents(analyzers, tree, cancellationToken)) { eventQueue.TryEnqueue(compilationEvent); } @@ -1076,7 +1090,11 @@ private AsyncQueue GetPendingEvents(ImmutableArray GetPendingEvents(ImmutableArray analyzers, bool includeSourceEvents, bool includeNonSourceEvents) + private AsyncQueue GetPendingEvents( + ImmutableArray analyzers, + bool includeSourceEvents, + bool includeNonSourceEvents, + CancellationToken cancellationToken) { Debug.Assert(includeSourceEvents || includeNonSourceEvents); @@ -1084,7 +1102,7 @@ private AsyncQueue GetPendingEvents(ImmutableArray