Skip to content

Commit 9713e84

Browse files
authored
Switch HashSet to ConcurrentDictionary for VS Extension Fix Cache (#581)
1 parent fba56c2 commit 9713e84

File tree

5 files changed

+56
-13
lines changed

5 files changed

+56
-13
lines changed

Changelog.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7+
## [1.0.19] - 2023-08-22
8+
### VS Extension
9+
Fix concurrent access issue with cache storage for fixes. Fix #480
10+
711
## [1.0.18] - 2023-08-09
812
### Rules
913
Fix language filtering on random number generator rules. Fix #468

DevSkim-DotNet/Microsoft.DevSkim.LanguageProtoInterop/CodeFixMapping.cs

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ public class MappingsVersion
1616
public Uri fileName;
1717
}
1818

19-
public class CodeFixMapping
19+
public class CodeFixMapping : IEquatable<CodeFixMapping>
2020
{
2121
/// <summary>
2222
/// Reported version of the document the diagnostic applies to
@@ -73,5 +73,36 @@ public CodeFixMapping(Diagnostic diagnostic, string replacement, Uri fileName, s
7373
this.matchEnd = matchEnd;
7474
this.isSuppression = isSuppression;
7575
}
76+
77+
public bool Equals(CodeFixMapping other)
78+
{
79+
if (ReferenceEquals(null, other)) return false;
80+
if (ReferenceEquals(this, other)) return true;
81+
return version == other.version && Equals(diagnostic, other.diagnostic) && replacement == other.replacement && Equals(fileName, other.fileName) && friendlyString == other.friendlyString && matchStart == other.matchStart && matchEnd == other.matchEnd && isSuppression == other.isSuppression;
82+
}
83+
84+
public override bool Equals(object obj)
85+
{
86+
if (ReferenceEquals(null, obj)) return false;
87+
if (ReferenceEquals(this, obj)) return true;
88+
if (obj.GetType() != this.GetType()) return false;
89+
return Equals((CodeFixMapping)obj);
90+
}
91+
92+
public override int GetHashCode()
93+
{
94+
unchecked
95+
{
96+
var hashCode = version.GetHashCode();
97+
hashCode = (hashCode * 397) ^ (diagnostic != null ? diagnostic.GetHashCode() : 0);
98+
hashCode = (hashCode * 397) ^ (replacement != null ? replacement.GetHashCode() : 0);
99+
hashCode = (hashCode * 397) ^ (fileName != null ? fileName.GetHashCode() : 0);
100+
hashCode = (hashCode * 397) ^ (friendlyString != null ? friendlyString.GetHashCode() : 0);
101+
hashCode = (hashCode * 397) ^ matchStart;
102+
hashCode = (hashCode * 397) ^ matchEnd;
103+
hashCode = (hashCode * 397) ^ isSuppression.GetHashCode();
104+
return hashCode;
105+
}
106+
}
76107
}
77108
}

DevSkim-DotNet/Microsoft.DevSkim.VisualStudio/DevSkimFixMessageTarget.cs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,15 +58,22 @@ await Task.Run(() =>
5858
{
5959
StaticData.FileToCodeFixMap.AddOrUpdate(mapping.fileName,
6060
// Add New Nested Dictionary
61-
(Uri _) => new ConcurrentDictionary<int, HashSet<CodeFixMapping>>(new Dictionary<int, HashSet<CodeFixMapping>>() { { mapping.version ?? -1, new HashSet<CodeFixMapping>() { mapping } } }),
61+
(Uri _) => new (new Dictionary<int, ConcurrentDictionary<CodeFixMapping, bool>>
62+
{ { mapping.version ?? -1, new (new Dictionary<CodeFixMapping, bool>()
63+
{ {mapping, true } }) } }),
6264
// Update Nested Dictionary
6365
(key, oldValue) =>
6466
{
6567
oldValue.AddOrUpdate(mapping.version ?? -1,
66-
// Add new HashSet
67-
(int _) => new HashSet<CodeFixMapping>() { mapping },
68-
// Update HashSet of CodeFixMappings
69-
(versionKey, oldSet) => { oldSet.Add(mapping); return oldSet; });
68+
// Add new Set of mappings
69+
(int _) =>
70+
{
71+
var addedMapping = new ConcurrentDictionary<CodeFixMapping, bool>();
72+
addedMapping.TryAdd(mapping, true);
73+
return addedMapping;
74+
},
75+
// Update Set of CodeFixMappings
76+
(versionKey, oldSet) => { oldSet.TryAdd(mapping, true); return oldSet; });
7077
return oldValue;
7178
});
7279
}

DevSkim-DotNet/Microsoft.DevSkim.VisualStudio/StaticData.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@
88
internal static class StaticData
99
{
1010
// Maps file name to a dictionary of file versions to a deduplicated set of CodeFixMappings
11-
internal static ConcurrentDictionary<Uri, ConcurrentDictionary<int, HashSet<CodeFixMapping>>> FileToCodeFixMap { get; } = new ConcurrentDictionary<Uri, ConcurrentDictionary<int, HashSet<CodeFixMapping>>>();
11+
internal static ConcurrentDictionary<Uri, ConcurrentDictionary<int, ConcurrentDictionary<CodeFixMapping, bool>>> FileToCodeFixMap { get; } = new();
1212
}
1313
}

DevSkim-DotNet/Microsoft.DevSkim.VisualStudio/SuggestionActionsSource.cs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using Microsoft.VisualStudio.Text.Editor;
77
using Microsoft.VisualStudio.Text.Operations;
88
using System;
9+
using System.Collections.Concurrent;
910
using System.Collections.Generic;
1011
using System.Linq;
1112
using System.Threading;
@@ -43,11 +44,11 @@ public IEnumerable<SuggestedActionSet> GetSuggestedActions(ISuggestedActionCateg
4344
List<ISuggestedAction> suggestedActions = new List<ISuggestedAction>();
4445
if (TryGetWordUnderCaret(out TextExtent wordExtent) && wordExtent.IsSignificant)
4546
{
46-
if (StaticData.FileToCodeFixMap.TryGetValue(new Uri(_fileName), out System.Collections.Concurrent.ConcurrentDictionary<int, HashSet<CodeFixMapping>> dictForFile))
47+
if (StaticData.FileToCodeFixMap.TryGetValue(new Uri(_fileName), out ConcurrentDictionary<int, ConcurrentDictionary<CodeFixMapping, bool>> dictForFile))
4748
{
48-
if (dictForFile.TryGetValue(wordExtent.Span.Snapshot.Version.VersionNumber, out HashSet<CodeFixMapping> fixes))
49+
if (dictForFile.TryGetValue(wordExtent.Span.Snapshot.Version.VersionNumber, out ConcurrentDictionary<CodeFixMapping, bool> fixes))
4950
{
50-
suggestedActions.AddRange(fixes.Where(codeFixMapping => Intersects(codeFixMapping, wordExtent)).Select(intersectedMapping => new DevSkimSuggestedAction(wordExtent.Span, intersectedMapping)));
51+
suggestedActions.AddRange(fixes.Where(codeFixMapping => Intersects(codeFixMapping.Key, wordExtent)).Select(intersectedMapping => new DevSkimSuggestedAction(wordExtent.Span, intersectedMapping.Key)));
5152
}
5253
}
5354
yield return new SuggestedActionSet(suggestedActions, wordExtent.Span);
@@ -79,11 +80,11 @@ public Task<bool> HasSuggestedActionsAsync(ISuggestedActionCategorySet requested
7980
bool res = TryGetWordUnderCaret(out TextExtent wordExtent);
8081
if (res && wordExtent.IsSignificant)
8182
{
82-
if (StaticData.FileToCodeFixMap.TryGetValue(new Uri(_fileName), out System.Collections.Concurrent.ConcurrentDictionary<int, HashSet<CodeFixMapping>> dictForFile))
83+
if (StaticData.FileToCodeFixMap.TryGetValue(new Uri(_fileName), out ConcurrentDictionary<int, ConcurrentDictionary<CodeFixMapping, bool>> dictForFile))
8384
{
84-
if (dictForFile.TryGetValue(wordExtent.Span.Snapshot.Version.VersionNumber, out HashSet<CodeFixMapping> fixes))
85+
if (dictForFile.TryGetValue(wordExtent.Span.Snapshot.Version.VersionNumber, out ConcurrentDictionary<CodeFixMapping, bool> fixes))
8586
{
86-
return fixes.Any(codeFixMapping => Intersects(codeFixMapping, wordExtent));
87+
return fixes.Any(codeFixMapping => Intersects(codeFixMapping.Key, wordExtent));
8788
}
8889
}
8990
}

0 commit comments

Comments
 (0)