forked from dotnet/roslyn
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Enable per-file state caching in analyzer. The state can be shared ac…
…ross analyzer actions and also across different analyzer instances. Fixes dotnet#6324
- Loading branch information
Showing
18 changed files
with
626 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
1 change: 0 additions & 1 deletion
1
src/Compilers/Core/Portable/DiagnosticAnalyzer/AdditionalText.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
55 changes: 55 additions & 0 deletions
55
src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalysisValueProvider.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.Runtime.CompilerServices; | ||
|
||
namespace Microsoft.CodeAnalysis.Diagnostics | ||
{ | ||
internal class AnalysisValueProvider<TKey, TValue> | ||
where TKey : class | ||
{ | ||
private readonly Func<TKey, TValue> _computeValue; | ||
|
||
// This provider holds a weak reference to the key-value pairs, as AnalysisValueProvider might outlive individual compilations. | ||
// CompilationAnalysisValueProvider, which wraps this provider and lives for the lifetime of specific compilation, holds a strong reference to the key-value pairs, providing an overall performance benefit. | ||
private readonly ConditionalWeakTable<TKey, WrappedValue> _valueCache; | ||
private readonly ConditionalWeakTable<TKey, WrappedValue>.CreateValueCallback _valueCacheCallback; | ||
|
||
internal IEqualityComparer<TKey> KeyComparer { get; private set; } | ||
|
||
public AnalysisValueProvider(Func<TKey, TValue> computeValue, IEqualityComparer<TKey> keyComparer) | ||
{ | ||
_computeValue = computeValue; | ||
KeyComparer = keyComparer ?? EqualityComparer<TKey>.Default; | ||
_valueCache = new ConditionalWeakTable<TKey, WrappedValue>(); | ||
_valueCacheCallback = new ConditionalWeakTable<TKey, WrappedValue>.CreateValueCallback(ComputeValue); | ||
} | ||
|
||
private sealed class WrappedValue | ||
{ | ||
public TValue Value { get; set; } | ||
} | ||
|
||
private WrappedValue ComputeValue(TKey key) | ||
{ | ||
var value = _computeValue(key); | ||
return new WrappedValue { Value = value }; | ||
} | ||
|
||
internal bool TryGetValue(TKey key, out TValue value) | ||
{ | ||
// Catch any exceptions from the computeValue callback, which calls into user code. | ||
try | ||
{ | ||
value = _valueCache.GetValue(key, _valueCacheCallback).Value; | ||
return true; | ||
} | ||
catch (Exception) | ||
{ | ||
value = default(TValue); | ||
return false; | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
50 changes: 50 additions & 0 deletions
50
src/Compilers/Core/Portable/DiagnosticAnalyzer/CompilationAnalysisValueProvider.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System.Collections.Generic; | ||
|
||
namespace Microsoft.CodeAnalysis.Diagnostics | ||
{ | ||
/// <summary> | ||
/// Wrapper over the core <see cref="AnalysisValueProvider{TKey, TValue}"/> which holds a strong reference to key-value pairs for the lifetime of a compilation that this provider is associated with. | ||
/// This ensures that values are never re-computed for equivalent keys while analyzing each compilation, improving overall analyzer performance. | ||
/// </summary> | ||
internal sealed class CompilationAnalysisValueProvider<TKey, TValue> | ||
where TKey : class | ||
{ | ||
private readonly AnalysisValueProvider<TKey, TValue> _analysisValueProvider; | ||
private readonly Dictionary<TKey, TValue> _valueMap; | ||
|
||
public CompilationAnalysisValueProvider(AnalysisValueProvider<TKey, TValue> analysisValueProvider) | ||
{ | ||
_analysisValueProvider = analysisValueProvider; | ||
_valueMap = new Dictionary<TKey, TValue>(analysisValueProvider.KeyComparer); | ||
} | ||
|
||
internal bool TryGetValue(TKey key, out TValue value) | ||
{ | ||
// First try to get the cached value for this compilation. | ||
lock (_valueMap) | ||
{ | ||
if (_valueMap.TryGetValue(key, out value)) | ||
{ | ||
return true; | ||
} | ||
} | ||
|
||
// Ask the core analysis value provider for the value. | ||
if (!_analysisValueProvider.TryGetValue(key, out value)) | ||
{ | ||
value = default(TValue); | ||
return false; | ||
} | ||
|
||
// Store the value for the lifetime of the compilation. | ||
lock (_valueMap) | ||
{ | ||
_valueMap[key] = value; | ||
} | ||
|
||
return true; | ||
} | ||
} | ||
} |
33 changes: 33 additions & 0 deletions
33
src/Compilers/Core/Portable/DiagnosticAnalyzer/CompilationAnalysisValueProviderFactory.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System.Collections.Generic; | ||
using System.Threading; | ||
|
||
namespace Microsoft.CodeAnalysis.Diagnostics | ||
{ | ||
internal sealed class CompilationAnalysisValueProviderFactory | ||
{ | ||
private Dictionary<object, object> _lazySharedStateProviderMap; | ||
|
||
public CompilationAnalysisValueProvider<TKey, TValue> GetValueProvider<TKey, TValue>(AnalysisValueProvider<TKey, TValue> analysisSharedStateProvider) | ||
where TKey : class | ||
{ | ||
if (_lazySharedStateProviderMap == null) | ||
{ | ||
Interlocked.CompareExchange(ref _lazySharedStateProviderMap, new Dictionary<object, object>(), null); | ||
} | ||
|
||
object value; | ||
lock (_lazySharedStateProviderMap) | ||
{ | ||
if (!_lazySharedStateProviderMap.TryGetValue(analysisSharedStateProvider, out value)) | ||
{ | ||
value = new CompilationAnalysisValueProvider<TKey, TValue>(analysisSharedStateProvider); | ||
_lazySharedStateProviderMap[analysisSharedStateProvider] = value; | ||
} | ||
} | ||
|
||
return value as CompilationAnalysisValueProvider<TKey, TValue>; | ||
} | ||
} | ||
} |
Oops, something went wrong.