Skip to content

Commit 265b67e

Browse files
authored
Merge pull request #2606 from MDoerner/ReworkParseCoordinatorPart2
Rework parse coordinator part2
2 parents 420f920 + 233bb27 commit 265b67e

File tree

3 files changed

+101
-30
lines changed

3 files changed

+101
-30
lines changed

RetailCoder.VBE/Refactorings/ExtractInterface/ExtractInterfaceRefactoring.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,6 @@ public void Refactor()
6565
pane.Selection = oldSelection.Value.Selection;
6666
}
6767
}
68-
69-
_state.OnParseRequested(this);
7068
}
7169

7270
public void Refactor(QualifiedSelection target)

Rubberduck.Parsing/VBA/ParseCoordinator.cs

Lines changed: 88 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ public class ParseCoordinator : IParseCoordinator
2828
public RubberduckParserState State { get { return _state; } }
2929

3030
private const int _maxDegreeOfParserParallelism = -1;
31+
private const int _maxDegreeOfModuleStateChangeParallelism = -1;
3132

3233
private readonly IDictionary<IVBComponent, IDictionary<Tuple<string, DeclarationType>, Attributes>> _componentAttributes
3334
= new Dictionary<IVBComponent, IDictionary<Tuple<string, DeclarationType>, Attributes>>();
@@ -79,7 +80,8 @@ private void ReparseRequested(object sender, EventArgs e)
7980
}
8081
else
8182
{
82-
Parse(_cancellationTokens[0]);
83+
Cancel();
84+
ParseInternal(_cancellationTokens[0]);
8385
}
8486
}
8587

@@ -100,7 +102,28 @@ private void Cancel(bool createNewTokenSource = true)
100102
/// <summary>
101103
/// For the use of tests only
102104
/// </summary>
105+
///
103106
public void Parse(CancellationTokenSource token)
107+
{
108+
SetSavedCancellationTokenSource(token);
109+
ParseInternal(token);
110+
}
111+
112+
private void SetSavedCancellationTokenSource(CancellationTokenSource token)
113+
{
114+
if (_cancellationTokens.Any())
115+
{
116+
_cancellationTokens[0].Cancel();
117+
_cancellationTokens[0].Dispose();
118+
_cancellationTokens[0] = token;
119+
}
120+
else
121+
{
122+
_cancellationTokens.Add(token);
123+
}
124+
}
125+
126+
private void ParseInternal(CancellationTokenSource token)
104127
{
105128
State.RefreshProjects(_vbe);
106129

@@ -163,10 +186,12 @@ private void RefreshDeclarationFinder()
163186

164187
private void SetModuleStates(List<IVBComponent> components, ParserState parserState)
165188
{
166-
foreach (var component in components)
167-
{
168-
State.SetModuleState(component, parserState);
169-
}
189+
var options = new ParallelOptions();
190+
options.MaxDegreeOfParallelism = _maxDegreeOfModuleStateChangeParallelism;
191+
192+
Parallel.ForEach(components, options, component => State.SetModuleState(component, parserState, null, false));
193+
194+
State.EvaluateParserState();
170195
}
171196

172197
private void CleanUpComponentAttributes(List<IVBComponent> components)
@@ -190,20 +215,34 @@ private void ClearComponentStateCacheForTests()
190215

191216
private void ParseComponents(List<IVBComponent> components, CancellationToken token)
192217
{
218+
SetModuleStates(components, ParserState.Parsing);
219+
193220
var options = new ParallelOptions();
194221
options.CancellationToken = token;
195222
options.MaxDegreeOfParallelism = _maxDegreeOfParserParallelism;
196223

197-
Parallel.ForEach(components,
198-
options,
199-
component =>
224+
try
225+
{
226+
Parallel.ForEach(components,
227+
options,
228+
component =>
229+
{
230+
State.ClearStateCache(component);
231+
var finishedParseTask = FinishedParseComponentTask(component, token);
232+
ProcessComponentParseResults(component, finishedParseTask);
233+
}
234+
);
235+
}
236+
catch (AggregateException exception)
237+
{
238+
if (exception.Flatten().InnerExceptions.All(ex => ex is OperationCanceledException))
200239
{
201-
State.SetModuleState(component, ParserState.Parsing);
202-
State.ClearStateCache(component);
203-
var finishedParseTask = FinishedParseComponentTask(component, token);
204-
ProcessComponentParseResults(component, finishedParseTask);
240+
return;
205241
}
206-
);
242+
throw;
243+
}
244+
245+
State.EvaluateParserState();
207246
}
208247

209248
private Task<ComponentParseTask.ParseCompletionArgs> FinishedParseComponentTask(IVBComponent component, CancellationToken token, TokenStreamRewriter rewriter = null)
@@ -239,6 +278,7 @@ private void ProcessComponentParseResults(IVBComponent component, Task<Component
239278
{
240279
if (finishedParseTask.IsFaulted)
241280
{
281+
//In contrast to the situation in the success scenario, the overall parser state is reevaluated immediately.
242282
State.SetModuleState(component, ParserState.Error, finishedParseTask.Exception.InnerException as SyntaxErrorException);
243283
}
244284
else if (finishedParseTask.IsCompleted)
@@ -255,7 +295,9 @@ private void ProcessComponentParseResults(IVBComponent component, Task<Component
255295
State.SetModuleAnnotations(component, result.Annotations);
256296

257297
// This really needs to go last
258-
State.SetModuleState(component, ParserState.Parsed);
298+
//It does not reevaluate the overall parer state to avoid concurrent evaluation of all module states and for performance reasons.
299+
//The evaluation has to be triggered manually in the calling procedure.
300+
State.SetModuleState(component, ParserState.Parsed, null, false);
259301
}
260302
}
261303
}
@@ -264,26 +306,48 @@ private void ProcessComponentParseResults(IVBComponent component, Task<Component
264306

265307
private void ResolveAllDeclarations(List<IVBComponent> components, CancellationToken token)
266308
{
309+
SetModuleStates(components, ParserState.ResolvingDeclarations);
310+
267311
var options = new ParallelOptions();
268312
options.CancellationToken = token;
269313
options.MaxDegreeOfParallelism = _maxDegreeOfParserParallelism;
270-
271-
Parallel.ForEach(components,
272-
options,
273-
component =>
314+
try
315+
{
316+
Parallel.ForEach(components,
317+
options,
318+
component =>
319+
{
320+
var qualifiedName = new QualifiedModuleName(component);
321+
ResolveDeclarations(qualifiedName.Component,
322+
State.ParseTrees.Find(s => s.Key == qualifiedName).Value);
323+
}
324+
);
325+
}
326+
catch (AggregateException exception)
327+
{
328+
if (exception.Flatten().InnerExceptions.All(ex => ex is OperationCanceledException))
274329
{
275-
var qualifiedName = new QualifiedModuleName(component);
276-
State.SetModuleState(component, ParserState.ResolvingDeclarations);
277-
ResolveDeclarations(qualifiedName.Component,
278-
State.ParseTrees.Find(s => s.Key == qualifiedName).Value);
330+
return;
279331
}
280-
);
332+
throw;
333+
}
281334
}
282335

283336

284337
private void ResolveReferences(CancellationToken token)
285338
{
286-
Task.WaitAll(ResolveReferencesAsync(token));
339+
try
340+
{
341+
Task.WaitAll(ResolveReferencesAsync(token));
342+
}
343+
catch (AggregateException exception)
344+
{
345+
if (exception.Flatten().InnerExceptions.All(ex => ex is OperationCanceledException))
346+
{
347+
return;
348+
}
349+
throw;
350+
}
287351
}
288352

289353

Rubberduck.Parsing/VBA/RubberduckParserState.cs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,7 @@ private void OnModuleStateChanged(IVBComponent component, ParserState state, Par
311311
}
312312
}
313313

314-
public void SetModuleState(IVBComponent component, ParserState state, SyntaxErrorException parserError = null)
314+
public void SetModuleState(IVBComponent component, ParserState state, SyntaxErrorException parserError = null, bool evaluateOverallState = true)
315315
{
316316
if (AllUserDeclarations.Count > 0)
317317
{
@@ -339,7 +339,7 @@ public void SetModuleState(IVBComponent component, ParserState state, SyntaxErro
339339
{
340340
// ghost component shouldn't even exist
341341
ClearStateCache(component);
342-
Status = EvaluateParserState();
342+
EvaluateParserState();
343343
return;
344344
}
345345
}
@@ -351,10 +351,18 @@ public void SetModuleState(IVBComponent component, ParserState state, SyntaxErro
351351
_moduleStates.AddOrUpdate(key, new ModuleState(parserError), (c, e) => e.SetModuleException(parserError));
352352
Logger.Debug("Module '{0}' state is changing to '{1}' (thread {2})", key.ComponentName, state, Thread.CurrentThread.ManagedThreadId);
353353
OnModuleStateChanged(component, state, oldState);
354-
Status = EvaluateParserState();
354+
if (evaluateOverallState)
355+
{
356+
EvaluateParserState();
357+
}
358+
}
359+
360+
public void EvaluateParserState()
361+
{
362+
lock (_statusLockObject) Status = OverallParserStateFromModuleStates();
355363
}
356364

357-
private ParserState EvaluateParserState()
365+
private ParserState OverallParserStateFromModuleStates()
358366
{
359367
if (_moduleStates.IsEmpty)
360368
{
@@ -497,6 +505,7 @@ public ParserState GetModuleState(IVBComponent component)
497505
return _moduleStates.GetOrAdd(new QualifiedModuleName(component), new ModuleState(ParserState.Pending)).State;
498506
}
499507

508+
private readonly object _statusLockObject = new object();
500509
private ParserState _status;
501510
public ParserState Status
502511
{

0 commit comments

Comments
 (0)