Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix ArgumentNullException under a race condition in analyzer driver #65083

Merged
merged 9 commits into from
Nov 12, 2022
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#nullable disable

using System.Collections.Generic;
using System.Diagnostics;

namespace Microsoft.CodeAnalysis.Diagnostics
{
Expand Down Expand Up @@ -56,6 +57,7 @@ public void ResetToReadyState()

public virtual void Free()
{
Debug.Assert(StateKind != StateKind.InProcess);
this.StateKind = StateKind.ReadyToProcess;
this.ProcessedActions.Clear();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ private static bool TryStartProcessingEntity_NoLock<TAnalysisEntity, TAnalyzerSt
return false;
}

private void MarkEntityProcessed<TAnalysisEntity, TAnalyzerStateData>(
private bool TryMarkEntityProcessed<TAnalysisEntity, TAnalyzerStateData>(
TAnalysisEntity analysisEntity,
Dictionary<TAnalysisEntity, TAnalyzerStateData?> pendingEntities,
ObjectPool<TAnalyzerStateData> pool)
Expand All @@ -144,11 +144,11 @@ private void MarkEntityProcessed<TAnalysisEntity, TAnalyzerStateData>(
{
lock (_gate)
{
MarkEntityProcessed_NoLock(analysisEntity, pendingEntities, pool);
return TryMarkEntityProcessed_NoLock(analysisEntity, pendingEntities, pool);
}
}

private static bool MarkEntityProcessed_NoLock<TAnalysisEntity, TAnalyzerStateData>(
private static bool TryMarkEntityProcessed_NoLock<TAnalysisEntity, TAnalyzerStateData>(
TAnalysisEntity analysisEntity,
Dictionary<TAnalysisEntity, TAnalyzerStateData?> pendingEntities,
ObjectPool<TAnalyzerStateData> pool)
Expand All @@ -157,12 +157,17 @@ private static bool MarkEntityProcessed_NoLock<TAnalysisEntity, TAnalyzerStateDa
{
if (pendingEntities.TryGetValue(analysisEntity, out var state))
{
if (state != null && state.StateKind == StateKind.InProcess)
{
// Still being processed.
return false;
}

pendingEntities.Remove(analysisEntity);
FreeState_NoLock(state, pool);
return true;
}

return false;
return true;
}

private bool TryStartSyntaxAnalysis_NoLock(SourceOrAdditionalFile file, [NotNullWhen(returnValue: true)] out AnalyzerStateData? state)
Expand Down Expand Up @@ -194,18 +199,25 @@ private bool TryStartSyntaxAnalysis_NoLock(SourceOrAdditionalFile file, [NotNull
return true;
}

private void MarkSyntaxAnalysisComplete_NoLock(SourceOrAdditionalFile file)
private bool TryMarkSyntaxAnalysisComplete_NoLock(SourceOrAdditionalFile file)
{
if (_pendingSyntaxAnalysisFilesCount == 0)
{
return;
// Already processed.
return true;
}

Debug.Assert(_lazyFilesWithAnalysisData != null);

var wasAlreadyFullyProcessed = false;
if (_lazyFilesWithAnalysisData.TryGetValue(file, out var state))
{
if (state.StateKind == StateKind.InProcess)
{
// Still being processed.
return false;
}

if (state.StateKind != StateKind.FullyProcessed)
{
FreeState_NoLock(state, _analyzerStateDataPool);
Expand All @@ -222,6 +234,7 @@ private void MarkSyntaxAnalysisComplete_NoLock(SourceOrAdditionalFile file)
}

_lazyFilesWithAnalysisData[file] = AnalyzerStateData.FullyProcessedInstance;
return true;
}

private Dictionary<int, DeclarationAnalyzerStateData> EnsureDeclarationDataMap_NoLock(ISymbol symbol, Dictionary<int, DeclarationAnalyzerStateData>? declarationDataMap)
Expand Down Expand Up @@ -269,21 +282,29 @@ private bool TryStartAnalyzingDeclaration_NoLock(
return true;
}

private void MarkDeclarationProcessed_NoLock(ISymbol symbol, int declarationIndex)
private bool TryMarkDeclarationProcessed_NoLock(ISymbol symbol, int declarationIndex)
{
if (!_pendingDeclarations.TryGetValue(symbol, out var declarationDataMap))
{
return;
// All declarations for the symbol have already been processed.
return true;
}

declarationDataMap = EnsureDeclarationDataMap_NoLock(symbol, declarationDataMap);

if (declarationDataMap.TryGetValue(declarationIndex, out var state))
{
if (state.StateKind == StateKind.InProcess)
{
// Still being processed.
return false;
}

FreeDeclarationAnalyzerState_NoLock(state);
}

declarationDataMap[declarationIndex] = DeclarationAnalyzerStateData.FullyProcessedInstance;
return true;
}

private void MarkDeclarationsProcessed_NoLock(ISymbol symbol)
Expand Down Expand Up @@ -375,9 +396,9 @@ public bool TryStartProcessingEvent(CompilationEvent compilationEvent, [NotNullW
return TryStartProcessingEntity(compilationEvent, _pendingEvents, _analyzerStateDataPool, out state);
}

public void MarkEventComplete(CompilationEvent compilationEvent)
public bool TryMarkEventComplete(CompilationEvent compilationEvent)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I converted all the methods that mark completion of events/symbols/declaration/syntax from void MarkXXXComplete(...) to bool TryMarkXXXComplete(...)

{
MarkEntityProcessed(compilationEvent, _pendingEvents, _analyzerStateDataPool);
return TryMarkEntityProcessed(compilationEvent, _pendingEvents, _analyzerStateDataPool);
}

public bool TryStartAnalyzingSymbol(ISymbol symbol, [NotNullWhen(returnValue: true)] out AnalyzerStateData? state)
Expand All @@ -391,17 +412,19 @@ public bool TryStartSymbolEndAnalysis(ISymbol symbol, [NotNullWhen(returnValue:
return TryStartProcessingEntity(symbol, _lazyPendingSymbolEndAnalyses, _analyzerStateDataPool, out state);
}

public void MarkSymbolComplete(ISymbol symbol)
public bool TryMarkSymbolComplete(ISymbol symbol)
{
MarkEntityProcessed(symbol, _pendingSymbols, _analyzerStateDataPool);
return TryMarkEntityProcessed(symbol, _pendingSymbols, _analyzerStateDataPool);
}

public void MarkSymbolEndAnalysisComplete(ISymbol symbol)
public bool TryMarkSymbolEndAnalysisComplete(ISymbol symbol)
{
if (_lazyPendingSymbolEndAnalyses != null)
{
MarkEntityProcessed(symbol, _lazyPendingSymbolEndAnalyses, _analyzerStateDataPool);
return TryMarkEntityProcessed(symbol, _lazyPendingSymbolEndAnalyses, _analyzerStateDataPool);
}

return true;
}

public bool TryStartAnalyzingDeclaration(
Expand All @@ -423,11 +446,11 @@ public bool IsDeclarationComplete(ISymbol symbol, int declarationIndex)
}
}

public void MarkDeclarationComplete(ISymbol symbol, int declarationIndex)
public bool TryMarkDeclarationComplete(ISymbol symbol, int declarationIndex)
{
lock (_gate)
{
MarkDeclarationProcessed_NoLock(symbol, declarationIndex);
return TryMarkDeclarationProcessed_NoLock(symbol, declarationIndex);
}
}

Expand All @@ -448,11 +471,11 @@ public bool TryStartSyntaxAnalysis(SourceOrAdditionalFile tree, [NotNullWhen(ret
}
}

public void MarkSyntaxAnalysisComplete(SourceOrAdditionalFile file)
public bool TryMarkSyntaxAnalysisComplete(SourceOrAdditionalFile file)
{
lock (_gate)
{
MarkSyntaxAnalysisComplete_NoLock(file);
return TryMarkSyntaxAnalysisComplete_NoLock(file);
}
}

Expand Down Expand Up @@ -561,7 +584,7 @@ private bool OnSymbolDeclaredEventProcessed_NoLock(SymbolDeclaredCompilationEven
MarkDeclarationsProcessed_NoLock(symbolDeclaredEvent.Symbol);

// Mark the symbol event completely processed.
return MarkEntityProcessed_NoLock(symbolDeclaredEvent, _pendingEvents, _analyzerStateDataPool);
return TryMarkEntityProcessed_NoLock(symbolDeclaredEvent, _pendingEvents, _analyzerStateDataPool);
}
}
}
Expand Down
Loading