Skip to content

Commit

Permalink
Swap the order of lock taken
Browse files Browse the repository at this point in the history
Move the Cancel() out of OnReparseRequest and SuspendParser method and into between the locks for both ParseAll and ParseInternal methods.
  • Loading branch information
bclothier committed Jun 20, 2018
1 parent 266ae63 commit 21160f9
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 18 deletions.
35 changes: 22 additions & 13 deletions Rubberduck.Parsing/VBA/ParseCoordinator.cs
Expand Up @@ -138,7 +138,9 @@ public void SuspendRequested(object sender, RubberduckStatusSuspendParserEventAr
_parserStateManager.SetStatusAndFireStateChanged(e.Requestor, ParserState.Busy,
CancellationToken.None);
e.BusyAction.Invoke();

}
finally
{
lock (_suspendStackSyncObject)
{
_isSuspended = false;
Expand All @@ -148,9 +150,7 @@ public void SuspendRequested(object sender, RubberduckStatusSuspendParserEventAr
parseRequestor = lastRequestor;
}
}
}
finally
{

if (_parsingSuspendLock.IsWriteLockHeld)
{
_parsingSuspendLock.ExitWriteLock();
Expand All @@ -159,7 +159,6 @@ public void SuspendRequested(object sender, RubberduckStatusSuspendParserEventAr

if (parseRequestor != null)
{
Cancel();
BeginParse(parseRequestor, _currentCancellationTokenSource.Token);
}
else if (originalStatus != ParserState.Ready)
Expand Down Expand Up @@ -231,11 +230,16 @@ private void ParseInternal(CancellationToken token)
var lockTaken = false;
try
{
Monitor.Enter(_parsingRunSyncObject, ref lockTaken);
if (!_parsingSuspendLock.IsWriteLockHeld)
{
_parsingSuspendLock.EnterReadLock();
}
lock (_cancellationSyncObject)
{
Cancel();
token = _currentCancellationTokenSource.Token;
}
Monitor.Enter(_parsingRunSyncObject, ref lockTaken);
ParseAllInternal(this, token);
}
catch (OperationCanceledException)
Expand All @@ -244,14 +248,14 @@ private void ParseInternal(CancellationToken token)
}
finally
{
if (_parsingSuspendLock.IsReadLockHeld)
{
_parsingSuspendLock.ExitReadLock();
}
if (lockTaken)
{
Monitor.Exit(_parsingRunSyncObject);
}
if (_parsingSuspendLock.IsReadLockHeld)
{
_parsingSuspendLock.ExitReadLock();
}
}
}

Expand Down Expand Up @@ -404,12 +408,17 @@ private void ParseAll(object requestor, CancellationToken token)
var lockTaken = false;
try
{
Monitor.Enter(_parsingRunSyncObject, ref lockTaken);
if (!_parsingSuspendLock.IsWriteLockHeld)
{
_parsingSuspendLock.EnterReadLock();
}

lock (_cancellationSyncObject)
{
Cancel();
token = _currentCancellationTokenSource.Token;
}
Monitor.Enter(_parsingRunSyncObject, ref lockTaken);

watch = Stopwatch.StartNew();
Logger.Debug("Parsing run started. (thread {0}).", Thread.CurrentThread.ManagedThreadId);

Expand All @@ -431,11 +440,11 @@ private void ParseAll(object requestor, CancellationToken token)
finally
{
if (watch != null && watch.IsRunning) watch.Stop();
if (lockTaken) Monitor.Exit(_parsingRunSyncObject);
if (_parsingSuspendLock.IsReadLockHeld)
{
_parsingSuspendLock.ExitReadLock();
}
if (lockTaken) Monitor.Exit(_parsingRunSyncObject);
}
if (watch != null) Logger.Debug("Parsing run finished after {0}s. (thread {1}).", watch.Elapsed.TotalSeconds, Thread.CurrentThread.ManagedThreadId);
}
Expand Down
11 changes: 6 additions & 5 deletions RubberduckTests/ParserState/ParserStateTests.cs
Expand Up @@ -162,6 +162,7 @@ public void Test_RPS_SuspendParser_Interrupted_Deadlock()
// unwanted inlining of the tasks.
// See: https://stackoverflow.com/questions/12245935/is-task-factory-startnew-guaranteed-to-use-another-thread-than-the-calling-thr
var source = new CancellationTokenSource();
var token = source.Token;
Task result2 = null;

state.StateChanged += (o, e) =>
Expand All @@ -173,20 +174,20 @@ public void Test_RPS_SuspendParser_Interrupted_Deadlock()
wasSuspensionExecuted =
state.OnSuspendParser(this, () => { wasSuspended = state.Status == ParserState.Busy; },
20);
}, source.Token);
result2.Wait(source.Token);
}, token);
result2.Wait(token);
}
};
var result1 = Task.Run(() =>
{
state.OnParseRequested(this);
}, source.Token);
result1.Wait(source.Token);
}, token);
result1.Wait(token);
while (result2 == null)
{
Thread.Sleep(1);
}
result2.Wait();
result2.Wait(token);
Assert.IsFalse(wasSuspended, "wasSuspended was set to true");
Assert.IsFalse(wasSuspensionExecuted, "wasSuspensionExecuted was set to true");
}
Expand Down

0 comments on commit 21160f9

Please sign in to comment.