From 0d5cc6b51d5934d5c39598703de7abcedd96b6a4 Mon Sep 17 00:00:00 2001 From: Max Doerner Date: Mon, 30 Jan 2017 22:41:30 +0100 Subject: [PATCH 1/8] Introduced option not to eval parser state on module state change and introduced method to trigger the evaluation manually. Moreover, made changing all module states concurrent. --- Rubberduck.Parsing/VBA/ParseCoordinator.cs | 23 +++++++++++++------ .../VBA/RubberduckParserState.cs | 17 ++++++++++---- 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/Rubberduck.Parsing/VBA/ParseCoordinator.cs b/Rubberduck.Parsing/VBA/ParseCoordinator.cs index e3ba1967a6..5edb21d720 100644 --- a/Rubberduck.Parsing/VBA/ParseCoordinator.cs +++ b/Rubberduck.Parsing/VBA/ParseCoordinator.cs @@ -28,6 +28,7 @@ public class ParseCoordinator : IParseCoordinator public RubberduckParserState State { get { return _state; } } private const int _maxDegreeOfParserParallelism = -1; + private const int _maxDegreeOfModuleStateChangeParallelism = -1; private readonly IDictionary, Attributes>> _componentAttributes = new Dictionary, Attributes>>(); @@ -163,10 +164,12 @@ private void RefreshDeclarationFinder() private void SetModuleStates(List components, ParserState parserState) { - foreach (var component in components) - { - State.SetModuleState(component, parserState); - } + var options = new ParallelOptions(); + options.MaxDegreeOfParallelism = _maxDegreeOfModuleStateChangeParallelism; + + Parallel.ForEach(components, options, component => State.SetModuleState(component, parserState, null, false)); + + State.EvaluateParserState(); } private void CleanUpComponentAttributes(List components) @@ -190,6 +193,8 @@ private void ClearComponentStateCacheForTests() private void ParseComponents(List components, CancellationToken token) { + SetModuleStates(components, ParserState.Parsing); + var options = new ParallelOptions(); options.CancellationToken = token; options.MaxDegreeOfParallelism = _maxDegreeOfParserParallelism; @@ -198,12 +203,12 @@ private void ParseComponents(List components, CancellationToken to options, component => { - State.SetModuleState(component, ParserState.Parsing); State.ClearStateCache(component); var finishedParseTask = FinishedParseComponentTask(component, token); ProcessComponentParseResults(component, finishedParseTask); } ); + State.EvaluateParserState(); } private Task FinishedParseComponentTask(IVBComponent component, CancellationToken token, TokenStreamRewriter rewriter = null) @@ -233,6 +238,7 @@ private void ProcessComponentParseResults(IVBComponent component, Task components, CancellationToken token) { + SetModuleStates(components, ParserState.ResolvingDeclarations); + var options = new ParallelOptions(); options.CancellationToken = token; options.MaxDegreeOfParallelism = _maxDegreeOfParserParallelism; @@ -267,7 +277,6 @@ private void ResolveAllDeclarations(List components, CancellationT component => { var qualifiedName = new QualifiedModuleName(component); - State.SetModuleState(component, ParserState.ResolvingDeclarations); ResolveDeclarations(qualifiedName.Component, State.ParseTrees.Find(s => s.Key == qualifiedName).Value); } diff --git a/Rubberduck.Parsing/VBA/RubberduckParserState.cs b/Rubberduck.Parsing/VBA/RubberduckParserState.cs index 2d4b087866..b451a690e8 100644 --- a/Rubberduck.Parsing/VBA/RubberduckParserState.cs +++ b/Rubberduck.Parsing/VBA/RubberduckParserState.cs @@ -311,7 +311,7 @@ private void OnModuleStateChanged(IVBComponent component, ParserState state, Par } } - public void SetModuleState(IVBComponent component, ParserState state, SyntaxErrorException parserError = null) + public void SetModuleState(IVBComponent component, ParserState state, SyntaxErrorException parserError = null, bool evaluateOverallState = true) { if (AllUserDeclarations.Count > 0) { @@ -339,7 +339,7 @@ public void SetModuleState(IVBComponent component, ParserState state, SyntaxErro { // ghost component shouldn't even exist ClearStateCache(component); - Status = EvaluateParserState(); + EvaluateParserState(); return; } } @@ -351,10 +351,18 @@ public void SetModuleState(IVBComponent component, ParserState state, SyntaxErro _moduleStates.AddOrUpdate(key, new ModuleState(parserError), (c, e) => e.SetModuleException(parserError)); Logger.Debug("Module '{0}' state is changing to '{1}' (thread {2})", key.ComponentName, state, Thread.CurrentThread.ManagedThreadId); OnModuleStateChanged(component, state, oldState); - Status = EvaluateParserState(); + if (evaluateOverallState) + { + EvaluateParserState(); + } + } + + public void EvaluateParserState() + { + lock (_statusLockObject) Status = OverallParserStateFromModuleStates(); } - private ParserState EvaluateParserState() + private ParserState OverallParserStateFromModuleStates() { if (_moduleStates.IsEmpty) { @@ -497,6 +505,7 @@ public ParserState GetModuleState(IVBComponent component) return _moduleStates.GetOrAdd(new QualifiedModuleName(component), new ModuleState(ParserState.Pending)).State; } + private readonly object _statusLockObject = new object(); private ParserState _status; public ParserState Status { From 32f9af9dea42c5e4c6e2fb84c3bba29dd2058ccc Mon Sep 17 00:00:00 2001 From: Max Doerner Date: Mon, 30 Jan 2017 23:30:04 +0100 Subject: [PATCH 2/8] Introduced cancellation on reparse in the tests. --- Rubberduck.Parsing/VBA/ParseCoordinator.cs | 24 +++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/Rubberduck.Parsing/VBA/ParseCoordinator.cs b/Rubberduck.Parsing/VBA/ParseCoordinator.cs index 5edb21d720..03b76cb12a 100644 --- a/Rubberduck.Parsing/VBA/ParseCoordinator.cs +++ b/Rubberduck.Parsing/VBA/ParseCoordinator.cs @@ -80,7 +80,8 @@ private void ReparseRequested(object sender, EventArgs e) } else { - Parse(_cancellationTokens[0]); + Cancel(); + ParseInternal(_cancellationTokens[0]); } } @@ -101,7 +102,28 @@ private void Cancel(bool createNewTokenSource = true) /// /// For the use of tests only /// + /// public void Parse(CancellationTokenSource token) + { + SetSavedCancellationTokenSource(token); + ParseInternal(token); + } + + private void SetSavedCancellationTokenSource(CancellationTokenSource token) + { + if (_cancellationTokens.Any()) + { + _cancellationTokens[0].Cancel(); + _cancellationTokens[0].Dispose(); + _cancellationTokens[0] = token; + } + else + { + _cancellationTokens.Add(token); + } + } + + private void ParseInternal(CancellationTokenSource token) { State.RefreshProjects(_vbe); From 2db64573b915336ebb5ba6241436752c1516273a Mon Sep 17 00:00:00 2001 From: Max Doerner Date: Tue, 31 Jan 2017 00:27:18 +0100 Subject: [PATCH 3/8] In ExtractInterface the ImplementInterface refactoring kicks in after resolving the declarations now instead after resolving the references. --- .../ExtractInterface/ExtractInterfaceRefactoring.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/RetailCoder.VBE/Refactorings/ExtractInterface/ExtractInterfaceRefactoring.cs b/RetailCoder.VBE/Refactorings/ExtractInterface/ExtractInterfaceRefactoring.cs index 12e2e79e7a..c2c22ccbb1 100644 --- a/RetailCoder.VBE/Refactorings/ExtractInterface/ExtractInterfaceRefactoring.cs +++ b/RetailCoder.VBE/Refactorings/ExtractInterface/ExtractInterfaceRefactoring.cs @@ -65,8 +65,6 @@ public void Refactor() pane.Selection = oldSelection.Value.Selection; } } - - _state.OnParseRequested(this); } public void Refactor(QualifiedSelection target) @@ -124,7 +122,7 @@ private void AddInterface() private int _insertionLine; private void _state_StateChanged(object sender, EventArgs e) { - if (_state.Status != ParserState.Ready) + if (_state.Status != ParserState.ResolvedDeclarations) { return; } From 71886b27962856c05faa1939592e13462c85f524 Mon Sep 17 00:00:00 2001 From: Max Doerner Date: Tue, 31 Jan 2017 21:55:02 +0100 Subject: [PATCH 4/8] Fixed bugs I introduced in the coordination of parse tasks. --- Rubberduck.Parsing/VBA/ComponentParseTask.cs | 10 ++++++++-- Rubberduck.Parsing/VBA/ParseCoordinator.cs | 11 +++++++++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/Rubberduck.Parsing/VBA/ComponentParseTask.cs b/Rubberduck.Parsing/VBA/ComponentParseTask.cs index 4643d782e7..1a88ff5641 100644 --- a/Rubberduck.Parsing/VBA/ComponentParseTask.cs +++ b/Rubberduck.Parsing/VBA/ComponentParseTask.cs @@ -101,9 +101,15 @@ public void Start(CancellationToken token) Cause = exception }); } - catch (OperationCanceledException) + catch (OperationCanceledException exception) { - // no results to be used, so no results "returned" + //We return this, so that the calling code knows that the operation actually has been cancelled. + var failedHandler = ParseFailure; + if (failedHandler != null) + failedHandler.Invoke(this, new ParseFailureArgs + { + Cause = exception + }); } } diff --git a/Rubberduck.Parsing/VBA/ParseCoordinator.cs b/Rubberduck.Parsing/VBA/ParseCoordinator.cs index aab5a24bc5..bbaa466040 100644 --- a/Rubberduck.Parsing/VBA/ParseCoordinator.cs +++ b/Rubberduck.Parsing/VBA/ParseCoordinator.cs @@ -215,7 +215,14 @@ private void ParseComponents(List components, CancellationToken to parser.ParseFailure += (sender, e) => { - tcs.SetException(e.Cause); + if (e.Cause is OperationCanceledException) + { + tcs.SetCanceled(); + } + else + { + tcs.SetException(e.Cause); + } }; parser.ParseCompleted += (sender, e) => { @@ -230,9 +237,9 @@ private void ParseComponents(List components, CancellationToken to private void ProcessComponentParseResults(IVBComponent component, Task finishedParseTask) { - finishedParseTask.Wait(); if (finishedParseTask.IsFaulted) { + var exception = finishedParseTask.Exception.InnerException; State.SetModuleState(component, ParserState.Error, finishedParseTask.Exception.InnerException as SyntaxErrorException); } else if (finishedParseTask.IsCompleted) From 22ec3a47d8a18ecc355438bdcc16ca302a446d21 Mon Sep 17 00:00:00 2001 From: Max Doerner Date: Tue, 31 Jan 2017 21:59:34 +0100 Subject: [PATCH 5/8] removed superfluous variable definition --- Rubberduck.Parsing/VBA/ParseCoordinator.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Rubberduck.Parsing/VBA/ParseCoordinator.cs b/Rubberduck.Parsing/VBA/ParseCoordinator.cs index bbaa466040..ee745ff8bf 100644 --- a/Rubberduck.Parsing/VBA/ParseCoordinator.cs +++ b/Rubberduck.Parsing/VBA/ParseCoordinator.cs @@ -239,7 +239,6 @@ private void ProcessComponentParseResults(IVBComponent component, Task Date: Tue, 31 Jan 2017 23:10:40 +0100 Subject: [PATCH 6/8] Added catching the OperationCanceledExceptions. --- Rubberduck.Parsing/VBA/ParseCoordinator.cs | 65 ++++++++++++++++------ 1 file changed, 49 insertions(+), 16 deletions(-) diff --git a/Rubberduck.Parsing/VBA/ParseCoordinator.cs b/Rubberduck.Parsing/VBA/ParseCoordinator.cs index 2308529798..dc55224a79 100644 --- a/Rubberduck.Parsing/VBA/ParseCoordinator.cs +++ b/Rubberduck.Parsing/VBA/ParseCoordinator.cs @@ -221,15 +221,27 @@ private void ParseComponents(List components, CancellationToken to options.CancellationToken = token; options.MaxDegreeOfParallelism = _maxDegreeOfParserParallelism; - Parallel.ForEach(components, - options, - component => + try + { + Parallel.ForEach(components, + options, + component => + { + State.ClearStateCache(component); + var finishedParseTask = FinishedParseComponentTask(component, token); + ProcessComponentParseResults(component, finishedParseTask); + } + ); + } + catch (AggregateException exception) + { + if (exception.Flatten().InnerExceptions.All(ex => ex is OperationCanceledException)) { - State.ClearStateCache(component); - var finishedParseTask = FinishedParseComponentTask(component, token); - ProcessComponentParseResults(component, finishedParseTask); + return; } - ); + throw; + } + State.EvaluateParserState(); } @@ -299,22 +311,43 @@ private void ResolveAllDeclarations(List components, CancellationT var options = new ParallelOptions(); options.CancellationToken = token; options.MaxDegreeOfParallelism = _maxDegreeOfParserParallelism; - - Parallel.ForEach(components, - options, - component => + try + { + Parallel.ForEach(components, + options, + component => + { + var qualifiedName = new QualifiedModuleName(component); + ResolveDeclarations(qualifiedName.Component, + State.ParseTrees.Find(s => s.Key == qualifiedName).Value); + } + ); + } + catch (AggregateException exception) + { + if (exception.Flatten().InnerExceptions.All(ex => ex is OperationCanceledException)) { - var qualifiedName = new QualifiedModuleName(component); - ResolveDeclarations(qualifiedName.Component, - State.ParseTrees.Find(s => s.Key == qualifiedName).Value); + return; } - ); + throw; + } } private void ResolveReferences(CancellationToken token) { - Task.WaitAll(ResolveReferencesAsync(token)); + try + { + Task.WaitAll(ResolveReferencesAsync(token)); + } + catch (AggregateException exception) + { + if (exception.Flatten().InnerExceptions.All(ex => ex is OperationCanceledException)) + { + return; + } + throw; + } } From 1bed4e7e55b12ccd9358f8d7418e57ff02d2e360 Mon Sep 17 00:00:00 2001 From: Max Doerner Date: Tue, 31 Jan 2017 23:19:44 +0100 Subject: [PATCH 7/8] Revert back to starting the implement interface refactoring only at the ready state. --- .../ExtractInterface/ExtractInterfaceRefactoring.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RetailCoder.VBE/Refactorings/ExtractInterface/ExtractInterfaceRefactoring.cs b/RetailCoder.VBE/Refactorings/ExtractInterface/ExtractInterfaceRefactoring.cs index c2c22ccbb1..788cc6afed 100644 --- a/RetailCoder.VBE/Refactorings/ExtractInterface/ExtractInterfaceRefactoring.cs +++ b/RetailCoder.VBE/Refactorings/ExtractInterface/ExtractInterfaceRefactoring.cs @@ -122,7 +122,7 @@ private void AddInterface() private int _insertionLine; private void _state_StateChanged(object sender, EventArgs e) { - if (_state.Status != ParserState.ResolvedDeclarations) + if (_state.Status != ParserState.Ready) { return; } From 4474634b9193a71fd524c8b835bf847a88d471d0 Mon Sep 17 00:00:00 2001 From: comintern Date: Tue, 31 Jan 2017 18:08:03 -0600 Subject: [PATCH 8/8] Add more thread safety to DeclarationFinder. --- .../Symbols/DeclarationFinder.cs | 112 ++++++++++-------- 1 file changed, 65 insertions(+), 47 deletions(-) diff --git a/Rubberduck.Parsing/Symbols/DeclarationFinder.cs b/Rubberduck.Parsing/Symbols/DeclarationFinder.cs index 9217db2fde..42cf8a6a8a 100644 --- a/Rubberduck.Parsing/Symbols/DeclarationFinder.cs +++ b/Rubberduck.Parsing/Symbols/DeclarationFinder.cs @@ -16,7 +16,7 @@ namespace Rubberduck.Parsing.Symbols internal static class DictionaryExtensions { public static IEnumerable AllValues( - this IDictionary source) + this ConcurrentDictionary> source) { return source.SelectMany(item => item.Value).ToList(); } @@ -26,6 +26,11 @@ public static IEnumerable AllValues( { return source.SelectMany(item => item.Value).ToList(); } + + public static ConcurrentDictionary> ToConcurrentDictionary(this IEnumerable> source) + { + return new ConcurrentDictionary>(source.Select(x => new KeyValuePair>(x.Key, new ConcurrentBag(x)))); + } } public class DeclarationFinder @@ -35,14 +40,14 @@ public class DeclarationFinder private readonly IHostApplication _hostApp; private readonly AnnotationService _annotationService; - private readonly ConcurrentDictionary _declarationsByName; - private readonly ConcurrentDictionary _declarations; - private readonly ConcurrentDictionary> _undeclared; + private readonly ConcurrentDictionary> _declarationsByName; + private readonly ConcurrentDictionary> _declarations; + private readonly ConcurrentDictionary> _undeclared; private readonly ConcurrentBag _unresolved; - private readonly ConcurrentDictionary _annotations; - private readonly ConcurrentDictionary _parametersByParent; - private readonly ConcurrentDictionary _userDeclarationsByType; - + private readonly ConcurrentDictionary> _annotations; + private readonly ConcurrentDictionary> _parametersByParent; + private readonly ConcurrentDictionary> _userDeclarationsByType; + private readonly Lazy> _handlersByWithEventsField; private readonly Lazy> _membersByImplementsContext; private readonly Lazy> _interfaceMembers; @@ -52,26 +57,16 @@ public class DeclarationFinder public DeclarationFinder(IReadOnlyList declarations, IEnumerable annotations, IHostApplication hostApp = null) { _hostApp = hostApp; - _annotations = new ConcurrentDictionary(annotations.GroupBy(node => node.QualifiedSelection.QualifiedName) - .ToDictionary(grouping => grouping.Key, grouping => grouping.ToArray())); - _declarations = new ConcurrentDictionary(declarations.GroupBy(item => item.QualifiedName.QualifiedModuleName) - .ToDictionary(grouping => grouping.Key, grouping => grouping.ToArray())); - - _declarationsByName = new ConcurrentDictionary( - declarations.GroupBy(declaration => new { IdentifierName = declaration.IdentifierName.ToLowerInvariant() }) - .ToDictionary(grouping => grouping.Key.IdentifierName, grouping => grouping.ToArray(), NameComparer)); - _parametersByParent = new ConcurrentDictionary( - declarations.Where(declaration => declaration.DeclarationType == DeclarationType.Parameter) - .GroupBy(declaration => declaration.ParentDeclaration) - .ToDictionary(grouping => grouping.Key, grouping => grouping.ToArray())); - _userDeclarationsByType = new ConcurrentDictionary( - declarations.Where(declaration => !declaration.IsBuiltIn) - .GroupBy(declaration => declaration.DeclarationType) - .ToDictionary(grouping => grouping.Key, grouping => grouping.ToArray())); + _annotations = annotations.GroupBy(node => node.QualifiedSelection.QualifiedName).ToConcurrentDictionary(); + _declarations = declarations.GroupBy(item => item.QualifiedName.QualifiedModuleName).ToConcurrentDictionary(); + _declarationsByName = declarations.GroupBy(declaration => declaration.IdentifierName.ToLowerInvariant()).ToConcurrentDictionary(); + _parametersByParent = declarations.Where(declaration => declaration.DeclarationType == DeclarationType.Parameter) + .GroupBy(declaration => declaration.ParentDeclaration).ToConcurrentDictionary(); + _userDeclarationsByType = declarations.Where(declaration => !declaration.IsBuiltIn).GroupBy(declaration => declaration.DeclarationType).ToConcurrentDictionary(); _builtinEvents = new Lazy>(() => FindBuiltInEventHandlers(declarations)); - _projects = _projects = declarations.Where(d => d.DeclarationType == DeclarationType.Project).ToList(); - _classes = _declarations.AllValues().Where(d => d.DeclarationType == DeclarationType.ClassModule).ToList(); + _projects = _projects = new Lazy>(() => new ConcurrentBag(declarations.Where(d => d.DeclarationType == DeclarationType.Project))); + _classes = new Lazy>(() => new ConcurrentBag(declarations.Where(d => d.DeclarationType == DeclarationType.ClassModule))); var withEventsFields = UserDeclarations(DeclarationType.Variable).Where(item => item.IsWithEvents).ToArray(); var events = withEventsFields.Select(field => @@ -95,7 +90,7 @@ public DeclarationFinder(IReadOnlyList declarations, IEnumerable item.WithEventsField, item => item.Handlers.ToArray()) )); - _undeclared = new ConcurrentDictionary>(new Dictionary>()); + _undeclared = new ConcurrentDictionary>(new Dictionary>()); _unresolved = new ConcurrentBag(new List()); _annotationService = new AnnotationService(this); @@ -111,7 +106,7 @@ public DeclarationFinder(IReadOnlyList declarations, IEnumerable>(() => - new ConcurrentDictionary(interfaceMembers.ToDictionary(item => item.InterfaceModule, item => item.InterfaceMembers.ToArray()))); + new ConcurrentDictionary(interfaceMembers.ToDictionary(item => item.InterfaceModule, item => item.InterfaceMembers.ToArray()))); var implementingNames = new Lazy>(() => implementsInstructions.SelectMany(item => _declarations[item.IdentifierReference.Declaration.QualifiedName.QualifiedModuleName] @@ -158,21 +153,40 @@ public IEnumerable FindBuiltinEventHandlers() } } - private readonly IEnumerable _classes; - public IEnumerable Classes { get { return _classes; } } + private readonly Lazy> _classes; + + public IEnumerable Classes + { + get + { + lock (ThreadLock) + { + return _classes.Value; + } + } + } + + private readonly Lazy> _projects; - private readonly IEnumerable _projects; - public IEnumerable Projects { get { return _projects; } } + public IEnumerable Projects + { + get + { + lock (ThreadLock) + { + return _projects.Value; + } + } + } public IEnumerable UserDeclarations(DeclarationType type) { - Declaration[] result; + ConcurrentBag result; if (!_userDeclarationsByType.TryGetValue(type, out result)) { - result = _userDeclarationsByType + result = new ConcurrentBag(_userDeclarationsByType .Where(item => item.Key.HasFlag(type)) - .SelectMany(item => item.Value) - .ToArray(); + .SelectMany(item => item.Value)); } return result; } @@ -218,7 +232,7 @@ public Declaration FindParameter(Declaration procedure, string parameterName) public IEnumerable FindMemberMatches(Declaration parent, string memberName) { - Declaration[] children; + ConcurrentBag children; if (_declarations.TryGetValue(parent.QualifiedName.QualifiedModuleName, out children)) { return children.Where(item => item.DeclarationType.HasFlag(DeclarationType.Member) @@ -230,7 +244,7 @@ public IEnumerable FindMemberMatches(Declaration parent, string mem public IEnumerable FindAnnotations(QualifiedModuleName module) { - IAnnotation[] result; + ConcurrentBag result; return _annotations.TryGetValue(module, out result) ? result : Enumerable.Empty(); } @@ -264,7 +278,7 @@ public Declaration FindLabel(Declaration procedure, string label) public IEnumerable MatchName(string name) { var normalizedName = ToNormalizedName(name); - Declaration[] result; + ConcurrentBag result; return _declarationsByName.TryGetValue(normalizedName, out result) ? result : Enumerable.Empty(); @@ -305,7 +319,7 @@ public Declaration FindStdModule(string name, Declaration parent = null, bool in { var matches = MatchName(name); result = matches.SingleOrDefault(declaration => declaration.DeclarationType.HasFlag(DeclarationType.ProceduralModule) - && (parent == null || parent.Equals(declaration.ParentDeclaration)) + && (parent.Equals(declaration.ParentDeclaration)) && (includeBuiltIn || !declaration.IsBuiltIn)); } catch (InvalidOperationException exception) @@ -324,7 +338,7 @@ public Declaration FindClassModule(string name, Declaration parent = null, bool { var matches = MatchName(name); result = matches.SingleOrDefault(declaration => declaration.DeclarationType.HasFlag(DeclarationType.ClassModule) - && (parent == null || parent.Equals(declaration.ParentDeclaration)) + && (parent.Equals(declaration.ParentDeclaration)) && (includeBuiltIn || !declaration.IsBuiltIn)); } catch (InvalidOperationException exception) @@ -481,18 +495,22 @@ public Declaration OnUndeclaredVariable(Declaration enclosingProcedure, string i var hasUndeclared = _undeclared.ContainsKey(enclosingProcedure.QualifiedName); if (hasUndeclared) { - var inScopeUndeclared = _undeclared[enclosingProcedure.QualifiedName].FirstOrDefault(d => d.IdentifierName == identifierName); + ConcurrentBag undeclared; + while (!_undeclared.TryGetValue(enclosingProcedure.QualifiedName, out undeclared)) + { + _undeclared.TryGetValue(enclosingProcedure.QualifiedName, out undeclared); + } + var inScopeUndeclared = undeclared.FirstOrDefault(d => d.IdentifierName == identifierName); if (inScopeUndeclared != null) { return inScopeUndeclared; } - _undeclared[enclosingProcedure.QualifiedName].Add(undeclaredLocal); + undeclared.Add(undeclaredLocal); } else { - _undeclared[enclosingProcedure.QualifiedName] = new List { undeclaredLocal }; + _undeclared.TryAdd(enclosingProcedure.QualifiedName, new ConcurrentBag { undeclaredLocal }); } - return undeclaredLocal; } @@ -524,14 +542,14 @@ public Declaration OnBracketedExpression(string expression, ParserRuleContext co var qualifiedName = hostApp.QualifiedName.QualifiedModuleName.QualifyMemberName(expression); - IList undeclared; + ConcurrentBag undeclared; if (_undeclared.TryGetValue(qualifiedName, out undeclared)) { return undeclared.SingleOrDefault(); } var item = new Declaration(qualifiedName, hostApp, hostApp, Tokens.Variant, string.Empty, false, false, Accessibility.Global, DeclarationType.BracketedExpression, context, context.GetSelection(), false, null); - _undeclared.TryAdd(qualifiedName, new List { item }); + _undeclared.TryAdd(qualifiedName, new ConcurrentBag { item }); return item; }