Skip to content

Commit 21160f9

Browse files
committed
Swap the order of lock taken
Move the Cancel() out of OnReparseRequest and SuspendParser method and into between the locks for both ParseAll and ParseInternal methods.
1 parent 266ae63 commit 21160f9

File tree

2 files changed

+28
-18
lines changed

2 files changed

+28
-18
lines changed

Rubberduck.Parsing/VBA/ParseCoordinator.cs

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,9 @@ public void SuspendRequested(object sender, RubberduckStatusSuspendParserEventAr
138138
_parserStateManager.SetStatusAndFireStateChanged(e.Requestor, ParserState.Busy,
139139
CancellationToken.None);
140140
e.BusyAction.Invoke();
141-
141+
}
142+
finally
143+
{
142144
lock (_suspendStackSyncObject)
143145
{
144146
_isSuspended = false;
@@ -148,9 +150,7 @@ public void SuspendRequested(object sender, RubberduckStatusSuspendParserEventAr
148150
parseRequestor = lastRequestor;
149151
}
150152
}
151-
}
152-
finally
153-
{
153+
154154
if (_parsingSuspendLock.IsWriteLockHeld)
155155
{
156156
_parsingSuspendLock.ExitWriteLock();
@@ -159,7 +159,6 @@ public void SuspendRequested(object sender, RubberduckStatusSuspendParserEventAr
159159

160160
if (parseRequestor != null)
161161
{
162-
Cancel();
163162
BeginParse(parseRequestor, _currentCancellationTokenSource.Token);
164163
}
165164
else if (originalStatus != ParserState.Ready)
@@ -231,11 +230,16 @@ private void ParseInternal(CancellationToken token)
231230
var lockTaken = false;
232231
try
233232
{
234-
Monitor.Enter(_parsingRunSyncObject, ref lockTaken);
235233
if (!_parsingSuspendLock.IsWriteLockHeld)
236234
{
237235
_parsingSuspendLock.EnterReadLock();
238236
}
237+
lock (_cancellationSyncObject)
238+
{
239+
Cancel();
240+
token = _currentCancellationTokenSource.Token;
241+
}
242+
Monitor.Enter(_parsingRunSyncObject, ref lockTaken);
239243
ParseAllInternal(this, token);
240244
}
241245
catch (OperationCanceledException)
@@ -244,14 +248,14 @@ private void ParseInternal(CancellationToken token)
244248
}
245249
finally
246250
{
247-
if (_parsingSuspendLock.IsReadLockHeld)
248-
{
249-
_parsingSuspendLock.ExitReadLock();
250-
}
251251
if (lockTaken)
252252
{
253253
Monitor.Exit(_parsingRunSyncObject);
254254
}
255+
if (_parsingSuspendLock.IsReadLockHeld)
256+
{
257+
_parsingSuspendLock.ExitReadLock();
258+
}
255259
}
256260
}
257261

@@ -404,12 +408,17 @@ private void ParseAll(object requestor, CancellationToken token)
404408
var lockTaken = false;
405409
try
406410
{
407-
Monitor.Enter(_parsingRunSyncObject, ref lockTaken);
408411
if (!_parsingSuspendLock.IsWriteLockHeld)
409412
{
410413
_parsingSuspendLock.EnterReadLock();
411414
}
412-
415+
lock (_cancellationSyncObject)
416+
{
417+
Cancel();
418+
token = _currentCancellationTokenSource.Token;
419+
}
420+
Monitor.Enter(_parsingRunSyncObject, ref lockTaken);
421+
413422
watch = Stopwatch.StartNew();
414423
Logger.Debug("Parsing run started. (thread {0}).", Thread.CurrentThread.ManagedThreadId);
415424

@@ -431,11 +440,11 @@ private void ParseAll(object requestor, CancellationToken token)
431440
finally
432441
{
433442
if (watch != null && watch.IsRunning) watch.Stop();
443+
if (lockTaken) Monitor.Exit(_parsingRunSyncObject);
434444
if (_parsingSuspendLock.IsReadLockHeld)
435445
{
436446
_parsingSuspendLock.ExitReadLock();
437447
}
438-
if (lockTaken) Monitor.Exit(_parsingRunSyncObject);
439448
}
440449
if (watch != null) Logger.Debug("Parsing run finished after {0}s. (thread {1}).", watch.Elapsed.TotalSeconds, Thread.CurrentThread.ManagedThreadId);
441450
}

RubberduckTests/ParserState/ParserStateTests.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ public void Test_RPS_SuspendParser_Interrupted_Deadlock()
162162
// unwanted inlining of the tasks.
163163
// See: https://stackoverflow.com/questions/12245935/is-task-factory-startnew-guaranteed-to-use-another-thread-than-the-calling-thr
164164
var source = new CancellationTokenSource();
165+
var token = source.Token;
165166
Task result2 = null;
166167

167168
state.StateChanged += (o, e) =>
@@ -173,20 +174,20 @@ public void Test_RPS_SuspendParser_Interrupted_Deadlock()
173174
wasSuspensionExecuted =
174175
state.OnSuspendParser(this, () => { wasSuspended = state.Status == ParserState.Busy; },
175176
20);
176-
}, source.Token);
177-
result2.Wait(source.Token);
177+
}, token);
178+
result2.Wait(token);
178179
}
179180
};
180181
var result1 = Task.Run(() =>
181182
{
182183
state.OnParseRequested(this);
183-
}, source.Token);
184-
result1.Wait(source.Token);
184+
}, token);
185+
result1.Wait(token);
185186
while (result2 == null)
186187
{
187188
Thread.Sleep(1);
188189
}
189-
result2.Wait();
190+
result2.Wait(token);
190191
Assert.IsFalse(wasSuspended, "wasSuspended was set to true");
191192
Assert.IsFalse(wasSuspensionExecuted, "wasSuspensionExecuted was set to true");
192193
}

0 commit comments

Comments
 (0)