Skip to content

Commit

Permalink
Avoid overlapping timers (#535)
Browse files Browse the repository at this point in the history
This fix avoids starting another callback until the current one has finished to avoid thread pool starvation.

We've automatically captured dumps where overlapping timers are all block on the same thread:

  ntdll.dll!_ZwWaitForMultipleObjects@20() Line 825 Unknown
  KERNELBASE.dll!WaitForMultipleObjectsEx(unsigned long nCount, void * const * lpHandles, int bWaitAll, unsigned long dwMilliseconds, int bAlertable) Line 1551 C
  [Managed to Native Transition]
  mscorlib.dll!System.Collections.Concurrent.ConcurrentDictionary<EnvDTE.Project, System.IO.FileSystemWatcher>.AcquireLocks(int fromInclusive, int toExclusive, ref int locksAcquired) Unknown
  mscorlib.dll!System.Collections.Concurrent.ConcurrentDictionary<EnvDTE.Project, System.IO.FileSystemWatcher>.AcquireAllLocks(ref int locksAcquired) Unknown
  mscorlib.dll!System.Collections.Concurrent.ConcurrentDictionary<EnvDTE.Project, System.IO.FileSystemWatcher>.GetValues() Unknown
> BundlerMinifierVsix.dll!BundlerMinifierVsix.Commands.ProjectEventCommand.TimerElapsed(object state) Unknown
  mscorlib.dll!System.Threading.TimerQueueTimer.CallCallbackInContext(object state) Unknown
  mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) Unknown
  mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) Unknown
  mscorlib.dll!System.Threading.TimerQueueTimer.CallCallback() Unknown
  mscorlib.dll!System.Threading.TimerQueueTimer.Fire() Unknown
  mscorlib.dll!System.Threading.TimerQueue.FireQueuedTimerCompletion(object state) Unknown
  mscorlib.dll!System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem() Unknown
  mscorlib.dll!System.Threading.ThreadPoolWorkQueue.Dispatch() Unknown
  mscorlib.dll!System.Threading._ThreadPoolWaitCallback.PerformWaitCallback() Unknown

In some dumps we saw over 1800 threads with the above stack.
  • Loading branch information
davkean committed Feb 16, 2021
1 parent 222e439 commit d0b49ba
Showing 1 changed file with 6 additions and 1 deletion.
7 changes: 6 additions & 1 deletion src/BundlerMinifierVsix/Commands/ProjectEventCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ private ProjectEventCommand(IServiceProvider provider)
_events.ProjectAdded += EnsureProjectIsActive;
_events.ProjectRemoved += OnProjectRemoved;

_timer = new Timer(TimerElapsed, null, 0, 250);
_timer = new Timer(TimerElapsed, null, 0, Timeout.Infinite);
}

public static ProjectEventCommand Instance { get; private set; }
Expand Down Expand Up @@ -207,6 +207,11 @@ void TimerElapsed(object state)
{
Logger.Log(ex);
}
finally
{
// Only now fire off next event to avoid overlapping timers
_timer.Change(250, Timeout.Infinite);
}
}

class QueueItem
Expand Down

0 comments on commit d0b49ba

Please sign in to comment.