-
Notifications
You must be signed in to change notification settings - Fork 4.1k
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 torn write of struct causing crash in symbol finder. #69927
Changes from 7 commits
f2b038a
ae7cb03
09eafaf
1963b05
2f69fab
9eb3cf0
9a053c4
53703eb
1127cf9
a8e8d41
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -3,16 +3,15 @@ | |||||
// See the LICENSE file in the project root for more information. | ||||||
|
||||||
using System; | ||||||
using System.Collections.Generic; | ||||||
using System.Collections.Immutable; | ||||||
using System.Diagnostics; | ||||||
using System.Linq; | ||||||
using System.Runtime.CompilerServices; | ||||||
using System.Threading; | ||||||
using System.Threading.Tasks; | ||||||
using Microsoft.CodeAnalysis.Collections; | ||||||
using Microsoft.CodeAnalysis.PooledObjects; | ||||||
using Microsoft.CodeAnalysis.Shared.Collections; | ||||||
using Microsoft.CodeAnalysis.Utilities; | ||||||
using Roslyn.Utilities; | ||||||
|
||||||
namespace Microsoft.CodeAnalysis.FindSymbols | ||||||
|
@@ -70,16 +69,10 @@ public MultiDictionary<string, ExtensionMethodInfo>.ValueSet GetExtensionMethodI | |||||
|
||||||
public bool ContainsExtensionMethod => _receiverTypeNameToExtensionMethodMap?.Count > 0; | ||||||
|
||||||
private SpellChecker? _spellChecker; | ||||||
private SpellChecker SpellChecker | ||||||
{ | ||||||
get | ||||||
{ | ||||||
_spellChecker ??= CreateSpellChecker(Checksum, _nodes); | ||||||
|
||||||
return _spellChecker.Value; | ||||||
} | ||||||
} | ||||||
/// <summary> | ||||||
/// Explicitly boxed so that we can safely initialize/read this across threads without the need for a lock. | ||||||
/// </summary> | ||||||
private StrongBox<SpellChecker>? _spellChecker; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ❗ Since There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The other option if you want to keep this a value type is to use this helper: roslyn/src/Compilers/Core/Portable/InternalUtilities/RoslynLazyInitializer.cs Lines 26 to 27 in bb804d5
The implementation of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks. Moved to just be a refernece type. That fits best here for me :) |
||||||
|
||||||
private SymbolTreeInfo( | ||||||
Checksum checksum, | ||||||
|
@@ -96,7 +89,7 @@ private SymbolTreeInfo( | |||||
private SymbolTreeInfo( | ||||||
Checksum checksum, | ||||||
ImmutableArray<Node> sortedNodes, | ||||||
SpellChecker? spellChecker, | ||||||
StrongBox<SpellChecker>? spellChecker, | ||||||
OrderPreservingMultiDictionary<int, int> inheritanceMap, | ||||||
MultiDictionary<string, ExtensionMethodInfo>? receiverTypeNameToExtensionMethodMap) | ||||||
{ | ||||||
|
@@ -181,11 +174,15 @@ private async Task<ImmutableArray<ISymbol>> FuzzyFindAsync( | |||||
using var similarNames = TemporaryArray<string>.Empty; | ||||||
using var result = TemporaryArray<ISymbol>.Empty; | ||||||
|
||||||
SpellChecker.FindSimilarWords(ref similarNames.AsRef(), name, substringsAreSimilar: false); | ||||||
// Ensure the spell checker is initialized. This is concurrency safe. Technically multiple threads may end | ||||||
// up overwriting the field, but even if that happens, we are sure to see a fully written spell checker as | ||||||
// the runtime guarantees that the strongbox .Value field will be completely written when we read out field. | ||||||
_spellChecker ??= CreateSpellChecker(Checksum, _nodes); | ||||||
_spellChecker.Value.FindSimilarWords(ref similarNames.AsRef(), name, substringsAreSimilar: false); | ||||||
|
||||||
foreach (var similarName in similarNames) | ||||||
{ | ||||||
var symbols = await FindAsync(lazyAssembly, similarName, ignoreCase: true, cancellationToken: cancellationToken).ConfigureAwait(false); | ||||||
var symbols = await FindAsync(lazyAssembly, similarName, ignoreCase: true, cancellationToken).ConfigureAwait(false); | ||||||
result.AddRange(symbols); | ||||||
} | ||||||
|
||||||
|
@@ -302,8 +299,8 @@ private static int BinarySearch(ImmutableArray<Node> nodes, string name) | |||||
|
||||||
#region Construction | ||||||
|
||||||
private static SpellChecker CreateSpellChecker(Checksum checksum, ImmutableArray<Node> sortedNodes) | ||||||
=> new(checksum, sortedNodes.Select(n => n.Name)); | ||||||
private static StrongBox<SpellChecker> CreateSpellChecker(Checksum checksum, ImmutableArray<Node> sortedNodes) | ||||||
=> new(new(checksum, sortedNodes.Select(n => n.Name))); | ||||||
|
||||||
private static ImmutableArray<Node> SortNodes(ImmutableArray<BuilderNode> unsortedNodes) | ||||||
{ | ||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SpellChecker?
was aNullable<SpellChecker>
. This is just a pure struct which can have torn writes. By moving to aStrongBox<SpellChecker>
we have teh runtime guarantee that if_spllChecker
is not null then_spellChecker.Value
is 'completely written'.