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 race condition in computing _hostAnalyzerStateMap #57648

Merged
merged 8 commits into from
Nov 11, 2021

Conversation

mavasani
Copy link
Contributor

@mavasani mavasani commented Nov 9, 2021

We recently moved the HostDiagnosticAnalyzers from workspace into solution state. However, the IDE diagnostic incremental analyzer still stores a _hostAnalyzerStateMap keyed only by the language name, which was based on the assumption that HostDiagnosticAnalyzers is a workspace level singleton. This leads to a race condition where in if the first instance of HostDiagnosticAnalyzers on the solution state doesn't include all built-in VS analyzer references, these analyzers will never be enabled in VS. With this change, we now include the HostDiagnosticAnalyzers from the solution state as part of the key for _hostAnalyzerStateMap, avoiding this race.

I noticed this while running some integration tests (compiler diagnostics go missing as compiler diagnostic analyzer is not identified). Additionally, we also seem to have few VS feedback tickets where compiler diagnostics are missing through the editing session, and this is a likely cause: AB#1431611 and AB#1430922.

We are considering taking this fix for servicing releases for Dev17.0 and Dev16.11 (needs approval).

mavasani and others added 2 commits November 10, 2021 08:30
…ementalAnalyzer.StateManager.cs

Co-authored-by: Andrew Hall <ryzngard@live.com>
@allisonchou allisonchou dismissed sharwell’s stale review November 11, 2021 01:26

The changes appear to be addressed and this fix needs to get in for servicing before tomorrow's deadline.

}

public string Language { get; }
public HostDiagnosticAnalyzers Analyzers { get; }
Copy link
Member

Choose a reason for hiding this comment

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

📝 I requested that instead of using HostDiagnosticAnalyzers as the key directly, we use HostDiagnosticAnalyzers.GetHostAnalyzerReferencesMap(). This has the same net result for the keys but ensures we don't accidentally GC root the DiagnosticAnalyzer instances themselves (from other fields in HostDiagnosticAnalyzers) in the off chance this cache ends up with unforeseen items.

@@ -15,7 +15,19 @@ internal partial class DiagnosticIncrementalAnalyzer
private partial class StateManager
{
public IEnumerable<StateSet> GetAllHostStateSets()
=> _hostAnalyzerStateMap.Values.SelectMany(v => v.OrderedStateSets);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@sharwell @ryzngard need your inputs here.

This was returning values for solution analyzers from all solution snapshots, instead of current solution, which led to duplicates being returned and subsequently an assert was firing at https://sourceroslyn.io/#Microsoft.CodeAnalysis.Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs,309 in integration tests.

This commit has one way to fix this issue. Another way to fix it would be to restore _hostAnalyzerStateMap back to only have the language name as the key as before, and introduce a separate field in the state manager for current solution state analyzer references. When asked to compute host state sets, we would first check if given project's solution state analyzer references are the same as ones stored onto this new field, otherwise we clear _hostAnalyzerStateMap and recompute.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think strongly tying the _hostAnalyzerStateMap to requirements of uniqueness makes sense in the way it's already done. It makes it very clear what the inputs used for the current state map are without having to look at multiple fields. I know it does add overhead of passing more values, but I think overall I still like the current approach.

…ementalAnalyzer.StateManager.HostStates.cs

Co-authored-by: Sam Harwell <sam@tunnelvisionlabs.com>
@jinujoseph jinujoseph merged commit bbd0654 into dotnet:release/dev17.0-vs-deps Nov 11, 2021
mavasani added a commit to mavasani/roslyn that referenced this pull request Dec 18, 2021
Fixes [AB#1449967](https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1449967)

dotnet#57648 fixed a race condition in computing the host analyzer references, but introduced a leak in _hostAnalyzerStateMap, wherein each new solution opened ends up creating a new entry in this map, even though the underlying set of host analyzer references doesn't change. This is due to the fact that each new SolutionState instance creates a new instance of HostDiagnosticAnalyzers, whose constructor creates a new map from analyzer references ID to the analyzer reference. This map is used as part of the key for _hostAnalyzerStateMap, leading to the leak.

This change fixes the leak by using the underlying list of host analyzer references as part of the key for _hostAnalyzerStateMap, which is reference equals across all solution instances opened in the VS session. Verified manually that after this fix, repeatedly opening and closing a solution in VS no longer leads to unbounded growth of _hostAnalyzerStateMap.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants