diff --git a/src/Features/Core/SolutionCrawler/WorkCoordinator.AsyncWorkItemQueue.cs b/src/Features/Core/SolutionCrawler/WorkCoordinator.AsyncWorkItemQueue.cs index 7f92db1748bc54b4a785a4ddb9881363442b6a97..da83ee27f284023453c662c86a59732c068f3578 100644 --- a/src/Features/Core/SolutionCrawler/WorkCoordinator.AsyncWorkItemQueue.cs +++ b/src/Features/Core/SolutionCrawler/WorkCoordinator.AsyncWorkItemQueue.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Roslyn.Utilities; @@ -104,7 +105,7 @@ public void RequestCancellationOnRunningTasks() lock (_gate) { // request to cancel all running works - _cancellationMap.Do(p => p.Value.Cancel()); + CancelAll_NoLock(); } } @@ -118,8 +119,7 @@ public void Dispose() Dispose_NoLock(); - _cancellationMap.Do(p => p.Value.Cancel()); - _cancellationMap.Clear(); + CancelAll_NoLock(); } } @@ -131,6 +131,24 @@ private bool HasAnyWork_NoLock } } + private void CancelAll_NoLock() + { + // nothing to do + if (_cancellationMap.Count == 0) + { + return; + } + + var cancellations = _cancellationMap.Values.ToList(); + + // it looks like Cancel can cause some code to run at the same thread, which can cause _cancellationMap to be changed. + // make a copy of the list and call cancellation + cancellations.Do(s => s.Cancel()); + + // clear cancellation map + _cancellationMap.Clear(); + } + protected void Cancel_NoLock(object key) { CancellationTokenSource source;