Remove strong reference to AnalyzerOptions in the analyzer manager ex…#20096
Remove strong reference to AnalyzerOptions in the analyzer manager ex…#20096mavasani merged 2 commits intodotnet:masterfrom
Conversation
…ecution context Fixes dotnet#20065
|
Test failure in https://ci.dot.net/job/dotnet_roslyn/job/master/job/windows_debug_vs-integration_prtest/5272/ with output: |
|
@dotnet-bot retest windows_debug_vs-integration_prtest please |
|
Tagging @dotnet/roslyn-compiler @dotnet/analyzer-ioperation for review. |
|
@heejaechang Can you please buddy test this change and double check analyzer execution in OOP with FSA turned on? I can share the locally built VSIX if you need. |
|
👍 sure, I will do the buddy testing. |
| @@ -20,7 +20,7 @@ private sealed class AnalyzerExecutionContext | |||
|
|
|||
| // This map stores the tasks to compute HostCompilationStartAnalysisScope for per-compilation analyzer actions, i.e. AnalyzerActions registered by analyzer's CompilationStartActions. | |||
| // Compilation start actions will get executed once per-each AnalyzerAndOptions as user might want to return different set of custom actions for each compilation/analyzer options. | |||
There was a problem hiding this comment.
Not sure if the comment needs an update here.
There was a problem hiding this comment.
I am going to delete a bunch of code around this in dev15.6 branch soon, we are going to get rid of the singleton.
I'm still not convinced this is what regressed it. Previously we were holding onto the Workspace, which would have still rooted the Solution anyways. |
Probably something else regressed it before that change, but nevertheless, we shouldn't be leaking anymore after this change. |
|
Tagging @MattGertz for approval |
|
confirmed that there is no PreviewWorkspace leaked with this change. |
|
@heejaechang - Thank you very much! |
gafter
left a comment
There was a problem hiding this comment.
The code change looks good to me.
I would expect this to be testable using a weak reference in the test - does a proper use of these APIs keep the solution alive? Is such a test feasible? Or would it naturally be a flaky test due to nondeterministic behavior of GC?
|
(I'm not signing off yet because I'd like to see if this is testable, and we need another sign-off from the compiler team) |
|
We do have https://github.com/dotnet/roslyn/blob/6a98a959cbd296991411e3e9d124aeeb4827ae1f/src/Test/Utilities/Portable/ObjectReference.cs as a tool you can use for asserting that objects are no longer held. It holds our current "GC until it goes away" pattern. |
|
@jasonmalinowski I see you meant this kind of lifetime check test. to explicitly see whether something left alive after certain operation. ya. I guess we could try to add this kind of test. |
|
Director Approved to Merge |
…ion instances from preview workspace.
|
@gafter @jasonmalinowski @heejaechang - Added a unit test |
|
@dotnet/roslyn-compiler Can I get another review? FYI that this fixes an Urgency-Now bug #20093 which already has director approval to merge. |
|
|
||
| public void ClearCompilationScopeMap(AnalyzerOptions analyzerOptions, Compilation compilation) | ||
| public void ClearCompilationScopeMap(Compilation compilation) | ||
| { |
There was a problem hiding this comment.
💡 This lock is now technically unnecessary (ConditionalWeakTable provides synchronization), but I think it would be confusing to some readers to remove it.
There was a problem hiding this comment.
Yes, leaving unchanged for now. I am going to remove a whole bunch of code in this file in dev15.6 soon.
| // This map stores the tasks to compute HostCompilationStartAnalysisScope for per-compilation analyzer actions, i.e. AnalyzerActions registered by analyzer's CompilationStartActions. | ||
| // Compilation start actions will get executed once per-each AnalyzerAndOptions as user might want to return different set of custom actions for each compilation/analyzer options. | ||
| private Dictionary<AnalyzerOptions, ConditionalWeakTable<Compilation, Task<HostCompilationStartAnalysisScope>>> _lazyCompilationScopeMap; | ||
| private ConditionalWeakTable<Compilation, Dictionary<AnalyzerOptions, Task<HostCompilationStartAnalysisScope>>> _lazyCompilationScopeCache; |
There was a problem hiding this comment.
❓ Is there any reason why this can't be private static readonly? More specifically, is it possible for two different AnalyzerExecutionContext instances to have the same Compilation instance with different maps associated?
There was a problem hiding this comment.
I am going to remove this map in a refactoring in dev15.6 very soon.
There was a problem hiding this comment.
Is there a bug tracking this we can reference here?
| ExecuteAnalyzers(previewWorkspace, analyzers); | ||
|
|
||
| previewWorkspace.Dispose(); | ||
| solutionObjectReference.AssertReleased(); |
There was a problem hiding this comment.
Consider asserting that the workspace is also disposed, just to make sure that's covered.
There was a problem hiding this comment.
I am explicitly disposing the workspace in the previous line. I am not sure what your recommendation is over here?
|
@gafter Any other feedback? |
|
Was Neal's feedback about tests addressed? |
|
@AlekseyTs - yes, I have added a unit test. |
|
I sign-off on Neal's behalf. |
…ecution context
Fixes #20065
Customer scenario
Performance: We are leaking solution instances in preview workspace (which is created every time we invoke a lightbulb preview). See #20065 (comment) for details.
Bugs this fixes:
Fixes #20065
Workarounds, if any
N/A
Risk
Low, as we are not changing any functionality here. Instead of having a
Dictionary<AnalyzerOptions, ConditionalWeakTable<Compilation, Task<HostCompilationStartAnalysisScope>>>with strong reference to AnalyzerOptions, we now use aConditionalWeakTable<Compilation, Dictionary<AnalyzerOptions, Task<HostCompilationStartAnalysisScope>>>, which has a weak reference and will let go AnalyzerOptions instance once the compilation is GC'ed.Performance impact
This should improve performance as we are preventing a leak of solutions in preview workspace.
Is this a regression from a previous update?
Yes, we recently refactored the WorkspaceAnalyzerOptions to strongly hold onto the solution snapshot. This was needed to fix an IDE race. This in turn is causing the map in question here to leak these solution instances.
Root cause analysis:
We don't have tests to catch and prevent leaks. We are going to further improve the design in this area when we tackle #2830 in 15.6, so we prevent such regressions in future.
How was the bug found?
Dogfooding.