From 34d7d9b40d64ed4def1583a113d784a84156ff3a Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Wed, 23 Jun 2021 12:31:11 -0700 Subject: [PATCH 1/5] Use TimeSpan instead of int for durations --- .../Workspaces/ProjectCacheServiceFactory.cs | 4 ++-- .../ProjectCacheHostServiceFactoryTests.cs | 12 +++++----- .../GlobalOperationAwareIdleProcessor.cs | 4 ++-- .../Portable/SolutionCrawler/IdleProcessor.cs | 22 +++++++++---------- ...rkCoordinator.AbstractPriorityProcessor.cs | 4 ++-- .../WorkCoordinator.HighPriorityProcessor.cs | 4 ++-- ...oordinator.IncrementalAnalyzerProcessor.cs | 12 +++++----- .../WorkCoordinator.LowPriorityProcessor.cs | 4 ++-- ...WorkCoordinator.NormalPriorityProcessor.cs | 4 ++-- ...WorkCoordinator.SemanticChangeProcessor.cs | 12 +++++----- .../SolutionCrawler/WorkCoordinator.cs | 14 ++++++------ .../ProjectCacheService.SimpleMRUCache.cs | 6 ++--- .../Portable/Workspace/ProjectCacheService.cs | 2 +- ...ualStudioProjectCacheHostServiceFactory.cs | 6 ++--- .../Remote/Core/SolutionChecksumUpdater.cs | 3 ++- .../Host/ProjectCacheHostServiceFactory.cs | 4 ++-- .../RemoteHostService.PerformanceReporter.cs | 2 +- 17 files changed, 60 insertions(+), 59 deletions(-) diff --git a/src/EditorFeatures/Core/Implementation/Workspaces/ProjectCacheServiceFactory.cs b/src/EditorFeatures/Core/Implementation/Workspaces/ProjectCacheServiceFactory.cs index aeff2305ff6a4..dfce03777c761 100644 --- a/src/EditorFeatures/Core/Implementation/Workspaces/ProjectCacheServiceFactory.cs +++ b/src/EditorFeatures/Core/Implementation/Workspaces/ProjectCacheServiceFactory.cs @@ -15,7 +15,7 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.Workspaces [Shared] internal partial class ProjectCacheHostServiceFactory : IWorkspaceServiceFactory { - private const int ImplicitCacheTimeoutInMS = 10000; + private static readonly TimeSpan s_implicitCacheTimeout = TimeSpan.FromMilliseconds(10000); [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] @@ -30,7 +30,7 @@ public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) return new ProjectCacheService(workspaceServices.Workspace); } - var service = new ProjectCacheService(workspaceServices.Workspace, ImplicitCacheTimeoutInMS); + var service = new ProjectCacheService(workspaceServices.Workspace, s_implicitCacheTimeout); // Also clear the cache when the solution is cleared or removed. workspaceServices.Workspace.WorkspaceChanged += (s, e) => diff --git a/src/EditorFeatures/Test/Workspaces/ProjectCacheHostServiceFactoryTests.cs b/src/EditorFeatures/Test/Workspaces/ProjectCacheHostServiceFactoryTests.cs index a01bff35edaa1..1f55e9d7d2332 100644 --- a/src/EditorFeatures/Test/Workspaces/ProjectCacheHostServiceFactoryTests.cs +++ b/src/EditorFeatures/Test/Workspaces/ProjectCacheHostServiceFactoryTests.cs @@ -30,7 +30,7 @@ private static void Test(Action new object()); @@ -113,7 +113,7 @@ public void TestCacheDoesNotKeepObjectsAliveAfterOwnerIsCollected2() public void TestImplicitCacheKeepsObjectAlive1() { var workspace = new AdhocWorkspace(MockHostServices.Instance, workspaceKind: WorkspaceKind.Host); - var cacheService = new ProjectCacheService(workspace, int.MaxValue); + var cacheService = new ProjectCacheService(workspace, TimeSpan.MaxValue); var reference = ObjectReference.CreateFromFactory(() => new object()); reference.UseReference(r => cacheService.CacheObjectIfCachingEnabledForKey(ProjectId.CreateNewId(), (object)null, r)); reference.AssertHeld(); @@ -125,7 +125,7 @@ public void TestImplicitCacheKeepsObjectAlive1() public void TestImplicitCacheMonitoring() { var workspace = new AdhocWorkspace(MockHostServices.Instance, workspaceKind: WorkspaceKind.Host); - var cacheService = new ProjectCacheService(workspace, 10); + var cacheService = new ProjectCacheService(workspace, TimeSpan.FromMilliseconds(10)); var weak = PutObjectInImplicitCache(cacheService); weak.AssertReleased(); @@ -156,7 +156,7 @@ public void TestP2PReference() var instanceTracker = ObjectReference.CreateFromFactory(() => new object()); - var cacheService = new ProjectCacheService(workspace, int.MaxValue); + var cacheService = new ProjectCacheService(workspace, TimeSpan.MaxValue); using (var cache = cacheService.EnableCaching(project2.Id)) { instanceTracker.UseReference(r => cacheService.CacheObjectIfCachingEnabledForKey(project1.Id, (object)null, r)); @@ -184,7 +184,7 @@ public void TestEjectFromImplicitCache() var weakLast = ObjectReference.Create(compilations[compilations.Count - 1]); var workspace = new AdhocWorkspace(MockHostServices.Instance, workspaceKind: WorkspaceKind.Host); - var cache = new ProjectCacheService(workspace, int.MaxValue); + var cache = new ProjectCacheService(workspace, TimeSpan.MaxValue); for (var i = 0; i < ProjectCacheService.ImplicitCacheSize + 1; i++) { cache.CacheObjectIfCachingEnabledForKey(ProjectId.CreateNewId(), (object)null, compilations[i]); @@ -212,7 +212,7 @@ public void TestCacheCompilationTwice() var weak1 = ObjectReference.Create(comp1); var workspace = new AdhocWorkspace(MockHostServices.Instance, workspaceKind: WorkspaceKind.Host); - var cache = new ProjectCacheService(workspace, int.MaxValue); + var cache = new ProjectCacheService(workspace, TimeSpan.MaxValue); var key = ProjectId.CreateNewId(); var owner = new object(); cache.CacheObjectIfCachingEnabledForKey(key, owner, comp1); diff --git a/src/Features/Core/Portable/SolutionCrawler/GlobalOperationAwareIdleProcessor.cs b/src/Features/Core/Portable/SolutionCrawler/GlobalOperationAwareIdleProcessor.cs index 99a212faeb0a3..a87224e638ec3 100644 --- a/src/Features/Core/Portable/SolutionCrawler/GlobalOperationAwareIdleProcessor.cs +++ b/src/Features/Core/Portable/SolutionCrawler/GlobalOperationAwareIdleProcessor.cs @@ -21,9 +21,9 @@ internal abstract class GlobalOperationAwareIdleProcessor : IdleProcessor public GlobalOperationAwareIdleProcessor( IAsynchronousOperationListener listener, IGlobalOperationNotificationService globalOperationNotificationService, - int backOffTimeSpanInMs, + TimeSpan backOffTimeSpan, CancellationToken shutdownToken) - : base(listener, backOffTimeSpanInMs, shutdownToken) + : base(listener, backOffTimeSpan, shutdownToken) { _globalOperation = null; _globalOperationTask = Task.CompletedTask; diff --git a/src/Features/Core/Portable/SolutionCrawler/IdleProcessor.cs b/src/Features/Core/Portable/SolutionCrawler/IdleProcessor.cs index 5eb3bc29f5447..ff96d9124b6ae 100644 --- a/src/Features/Core/Portable/SolutionCrawler/IdleProcessor.cs +++ b/src/Features/Core/Portable/SolutionCrawler/IdleProcessor.cs @@ -12,28 +12,28 @@ namespace Microsoft.CodeAnalysis.SolutionCrawler { internal abstract class IdleProcessor { - private const int MinimumDelayInMS = 50; + private static readonly TimeSpan s_minimumDelay = TimeSpan.FromMilliseconds(50); protected readonly IAsynchronousOperationListener Listener; protected readonly CancellationToken CancellationToken; - protected readonly int BackOffTimeSpanInMS; + protected readonly TimeSpan BackOffTimeSpan; // points to processor task private Task? _processorTask; // there is one thread that writes to it and one thread reads from it - private int _lastAccessTimeInMS; + private SharedStopwatch _timeSinceLastAccess; public IdleProcessor( IAsynchronousOperationListener listener, - int backOffTimeSpanInMS, + TimeSpan backOffTimeSpan, CancellationToken cancellationToken) { Listener = listener; CancellationToken = cancellationToken; - BackOffTimeSpanInMS = backOffTimeSpanInMS; - _lastAccessTimeInMS = Environment.TickCount; + BackOffTimeSpan = backOffTimeSpan; + _timeSinceLastAccess = SharedStopwatch.StartNew(); } protected abstract Task WaitAsync(CancellationToken cancellationToken); @@ -46,7 +46,7 @@ protected void Start() } protected void UpdateLastAccessTime() - => _lastAccessTimeInMS = Environment.TickCount; + => _timeSinceLastAccess = SharedStopwatch.StartNew(); protected async Task WaitForIdleAsync(IExpeditableDelaySource expeditableDelaySource) { @@ -57,15 +57,15 @@ protected async Task WaitForIdleAsync(IExpeditableDelaySource expeditableDelaySo return; } - var diffInMS = Environment.TickCount - _lastAccessTimeInMS; - if (diffInMS >= BackOffTimeSpanInMS) + var diff = _timeSinceLastAccess.Elapsed; + if (diff >= BackOffTimeSpan) { return; } // TODO: will safestart/unwarp capture cancellation exception? - var timeLeft = BackOffTimeSpanInMS - diffInMS; - if (!await expeditableDelaySource.Delay(TimeSpan.FromMilliseconds(Math.Max(MinimumDelayInMS, timeLeft)), CancellationToken).ConfigureAwait(false)) + var timeLeft = BackOffTimeSpan - diff; + if (!await expeditableDelaySource.Delay(TimeSpan.FromMilliseconds(Math.Max(s_minimumDelay.TotalMilliseconds, timeLeft.TotalMilliseconds)), CancellationToken).ConfigureAwait(false)) { // The delay terminated early to accommodate a blocking operation. Make sure to delay long // enough that low priority (on idle) operations get a chance to be triggered. diff --git a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AbstractPriorityProcessor.cs b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AbstractPriorityProcessor.cs index 10f16c389c9cd..d84446da3ba92 100644 --- a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AbstractPriorityProcessor.cs +++ b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AbstractPriorityProcessor.cs @@ -30,9 +30,9 @@ public AbstractPriorityProcessor( IncrementalAnalyzerProcessor processor, Lazy> lazyAnalyzers, IGlobalOperationNotificationService globalOperationNotificationService, - int backOffTimeSpanInMs, + TimeSpan backOffTimeSpan, CancellationToken shutdownToken) - : base(listener, globalOperationNotificationService, backOffTimeSpanInMs, shutdownToken) + : base(listener, globalOperationNotificationService, backOffTimeSpan, shutdownToken) { _gate = new object(); _lazyAnalyzers = lazyAnalyzers; diff --git a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.HighPriorityProcessor.cs b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.HighPriorityProcessor.cs index b857367086186..33fe20c27b821 100644 --- a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.HighPriorityProcessor.cs +++ b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.HighPriorityProcessor.cs @@ -35,9 +35,9 @@ public HighPriorityProcessor( IAsynchronousOperationListener listener, IncrementalAnalyzerProcessor processor, Lazy> lazyAnalyzers, - int backOffTimeSpanInMs, + TimeSpan backOffTimeSpan, CancellationToken shutdownToken) - : base(listener, backOffTimeSpanInMs, shutdownToken) + : base(listener, backOffTimeSpan, shutdownToken) { _processor = processor; _lazyAnalyzers = lazyAnalyzers; diff --git a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.IncrementalAnalyzerProcessor.cs b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.IncrementalAnalyzerProcessor.cs index 0b5f7d08d5f4a..bb5892d87acfb 100644 --- a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.IncrementalAnalyzerProcessor.cs +++ b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.IncrementalAnalyzerProcessor.cs @@ -50,9 +50,9 @@ public IncrementalAnalyzerProcessor( IEnumerable> analyzerProviders, bool initializeLazily, Registration registration, - int highBackOffTimeSpanInMs, - int normalBackOffTimeSpanInMs, - int lowBackOffTimeSpanInMs, + TimeSpan highBackOffTimeSpan, + TimeSpan normalBackOffTimeSpan, + TimeSpan lowBackOffTimeSpan, CancellationToken shutdownToken) { _logAggregator = new LogAggregator(); @@ -81,9 +81,9 @@ public IncrementalAnalyzerProcessor( var globalNotificationService = _registration.Workspace.Services.GetRequiredService(); - _highPriorityProcessor = new HighPriorityProcessor(listener, this, lazyActiveFileAnalyzers, highBackOffTimeSpanInMs, shutdownToken); - _normalPriorityProcessor = new NormalPriorityProcessor(listener, this, lazyAllAnalyzers, globalNotificationService, normalBackOffTimeSpanInMs, shutdownToken); - _lowPriorityProcessor = new LowPriorityProcessor(listener, this, lazyAllAnalyzers, globalNotificationService, lowBackOffTimeSpanInMs, shutdownToken); + _highPriorityProcessor = new HighPriorityProcessor(listener, this, lazyActiveFileAnalyzers, highBackOffTimeSpan, shutdownToken); + _normalPriorityProcessor = new NormalPriorityProcessor(listener, this, lazyAllAnalyzers, globalNotificationService, normalBackOffTimeSpan, shutdownToken); + _lowPriorityProcessor = new LowPriorityProcessor(listener, this, lazyAllAnalyzers, globalNotificationService, lowBackOffTimeSpan, shutdownToken); } private static IDiagnosticAnalyzerService? GetDiagnosticAnalyzerService(IEnumerable> analyzerProviders) diff --git a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.LowPriorityProcessor.cs b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.LowPriorityProcessor.cs index 19b00ffb7a35b..de3db50989511 100644 --- a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.LowPriorityProcessor.cs +++ b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.LowPriorityProcessor.cs @@ -30,9 +30,9 @@ public LowPriorityProcessor( IncrementalAnalyzerProcessor processor, Lazy> lazyAnalyzers, IGlobalOperationNotificationService globalOperationNotificationService, - int backOffTimeSpanInMs, + TimeSpan backOffTimeSpan, CancellationToken shutdownToken) - : base(listener, processor, lazyAnalyzers, globalOperationNotificationService, backOffTimeSpanInMs, shutdownToken) + : base(listener, processor, lazyAnalyzers, globalOperationNotificationService, backOffTimeSpan, shutdownToken) { _workItemQueue = new AsyncProjectWorkItemQueue(processor._registration.ProgressReporter, processor._registration.Workspace); diff --git a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.NormalPriorityProcessor.cs b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.NormalPriorityProcessor.cs index 213ac6b540b6f..681e3ae8c5d49 100644 --- a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.NormalPriorityProcessor.cs +++ b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.NormalPriorityProcessor.cs @@ -50,9 +50,9 @@ public NormalPriorityProcessor( IncrementalAnalyzerProcessor processor, Lazy> lazyAnalyzers, IGlobalOperationNotificationService globalOperationNotificationService, - int backOffTimeSpanInMs, + TimeSpan backOffTimeSpan, CancellationToken shutdownToken) - : base(listener, processor, lazyAnalyzers, globalOperationNotificationService, backOffTimeSpanInMs, shutdownToken) + : base(listener, processor, lazyAnalyzers, globalOperationNotificationService, backOffTimeSpan, shutdownToken) { _running = Task.CompletedTask; _workItemQueue = new AsyncDocumentWorkItemQueue(processor._registration.ProgressReporter, processor._registration.Workspace); diff --git a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.SemanticChangeProcessor.cs b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.SemanticChangeProcessor.cs index b5cf17f1660fd..25bdd62a21fe3 100644 --- a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.SemanticChangeProcessor.cs +++ b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.SemanticChangeProcessor.cs @@ -41,16 +41,16 @@ public SemanticChangeProcessor( IAsynchronousOperationListener listener, Registration registration, IncrementalAnalyzerProcessor documentWorkerProcessor, - int backOffTimeSpanInMS, - int projectBackOffTimeSpanInMS, + TimeSpan backOffTimeSpan, + TimeSpan projectBackOffTimeSpan, CancellationToken cancellationToken) - : base(listener, backOffTimeSpanInMS, cancellationToken) + : base(listener, backOffTimeSpan, cancellationToken) { _gate = new SemaphoreSlim(initialCount: 0); _registration = registration; - _processor = new ProjectProcessor(listener, registration, documentWorkerProcessor, projectBackOffTimeSpanInMS, cancellationToken); + _processor = new ProjectProcessor(listener, registration, documentWorkerProcessor, projectBackOffTimeSpan, cancellationToken); _workGate = new NonReentrantLock(); _pendingWork = new Dictionary(); @@ -352,9 +352,9 @@ public ProjectProcessor( IAsynchronousOperationListener listener, Registration registration, IncrementalAnalyzerProcessor processor, - int backOffTimeSpanInMS, + TimeSpan backOffTimeSpan, CancellationToken cancellationToken) - : base(listener, backOffTimeSpanInMS, cancellationToken) + : base(listener, backOffTimeSpan, cancellationToken) { _registration = registration; _processor = processor; diff --git a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.cs b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.cs index 7cf786abe40fe..51bdc2490fb4e 100644 --- a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.cs +++ b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.cs @@ -57,18 +57,18 @@ public WorkCoordinator( _eventProcessingQueue = new TaskQueue(listener, TaskScheduler.Default); - var activeFileBackOffTimeSpanInMS = _optionService.GetOption(InternalSolutionCrawlerOptions.ActiveFileWorkerBackOffTimeSpanInMS); - var allFilesWorkerBackOffTimeSpanInMS = _optionService.GetOption(InternalSolutionCrawlerOptions.AllFilesWorkerBackOffTimeSpanInMS); - var entireProjectWorkerBackOffTimeSpanInMS = _optionService.GetOption(InternalSolutionCrawlerOptions.EntireProjectWorkerBackOffTimeSpanInMS); + var activeFileBackOffTimeSpan = TimeSpan.FromMilliseconds(_optionService.GetOption(InternalSolutionCrawlerOptions.ActiveFileWorkerBackOffTimeSpanInMS)); + var allFilesWorkerBackOffTimeSpan = TimeSpan.FromMilliseconds(_optionService.GetOption(InternalSolutionCrawlerOptions.AllFilesWorkerBackOffTimeSpanInMS)); + var entireProjectWorkerBackOffTimeSpan = TimeSpan.FromMilliseconds(_optionService.GetOption(InternalSolutionCrawlerOptions.EntireProjectWorkerBackOffTimeSpanInMS)); _documentAndProjectWorkerProcessor = new IncrementalAnalyzerProcessor( listener, analyzerProviders, initializeLazily, _registration, - activeFileBackOffTimeSpanInMS, allFilesWorkerBackOffTimeSpanInMS, entireProjectWorkerBackOffTimeSpanInMS, _shutdownToken); + activeFileBackOffTimeSpan, allFilesWorkerBackOffTimeSpan, entireProjectWorkerBackOffTimeSpan, _shutdownToken); - var semanticBackOffTimeSpanInMS = _optionService.GetOption(InternalSolutionCrawlerOptions.SemanticChangeBackOffTimeSpanInMS); - var projectBackOffTimeSpanInMS = _optionService.GetOption(InternalSolutionCrawlerOptions.ProjectPropagationBackOffTimeSpanInMS); + var semanticBackOffTimeSpan = TimeSpan.FromMilliseconds(_optionService.GetOption(InternalSolutionCrawlerOptions.SemanticChangeBackOffTimeSpanInMS)); + var projectBackOffTimeSpan = TimeSpan.FromMilliseconds(_optionService.GetOption(InternalSolutionCrawlerOptions.ProjectPropagationBackOffTimeSpanInMS)); - _semanticChangeProcessor = new SemanticChangeProcessor(listener, _registration, _documentAndProjectWorkerProcessor, semanticBackOffTimeSpanInMS, projectBackOffTimeSpanInMS, _shutdownToken); + _semanticChangeProcessor = new SemanticChangeProcessor(listener, _registration, _documentAndProjectWorkerProcessor, semanticBackOffTimeSpan, projectBackOffTimeSpan, _shutdownToken); // if option is on if (_optionService.GetOption(InternalSolutionCrawlerOptions.SolutionCrawler)) diff --git a/src/Features/Core/Portable/Workspace/ProjectCacheService.SimpleMRUCache.cs b/src/Features/Core/Portable/Workspace/ProjectCacheService.SimpleMRUCache.cs index 0121f4f09e907..ed90a6a80e861 100644 --- a/src/Features/Core/Portable/Workspace/ProjectCacheService.SimpleMRUCache.cs +++ b/src/Features/Core/Portable/Workspace/ProjectCacheService.SimpleMRUCache.cs @@ -93,9 +93,9 @@ private class ImplicitCacheMonitor : IdleProcessor private readonly ProjectCacheService _owner; private readonly SemaphoreSlim _gate; - public ImplicitCacheMonitor(ProjectCacheService owner, int backOffTimeSpanInMS) + public ImplicitCacheMonitor(ProjectCacheService owner, TimeSpan backOffTimeSpan) : base(AsynchronousOperationListenerProvider.NullListener, - backOffTimeSpanInMS, + backOffTimeSpan, CancellationToken.None) { _owner = owner; @@ -106,7 +106,7 @@ public ImplicitCacheMonitor(ProjectCacheService owner, int backOffTimeSpanInMS) protected override Task ExecuteAsync() { - _owner.ClearExpiredImplicitCache(DateTime.UtcNow - TimeSpan.FromMilliseconds(BackOffTimeSpanInMS)); + _owner.ClearExpiredImplicitCache(DateTime.UtcNow - BackOffTimeSpan); return Task.CompletedTask; } diff --git a/src/Features/Core/Portable/Workspace/ProjectCacheService.cs b/src/Features/Core/Portable/Workspace/ProjectCacheService.cs index 555fffd75022e..dffbc15cc5c51 100644 --- a/src/Features/Core/Portable/Workspace/ProjectCacheService.cs +++ b/src/Features/Core/Portable/Workspace/ProjectCacheService.cs @@ -34,7 +34,7 @@ internal partial class ProjectCacheService : IProjectCacheHostService public ProjectCacheService(Workspace workspace) => _workspace = workspace; - public ProjectCacheService(Workspace workspace, int implicitCacheTimeout) + public ProjectCacheService(Workspace workspace, TimeSpan implicitCacheTimeout) { _workspace = workspace; diff --git a/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioProjectCacheHostServiceFactory.cs b/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioProjectCacheHostServiceFactory.cs index 23b3b6b1a8f8e..3d9a0808a469d 100644 --- a/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioProjectCacheHostServiceFactory.cs +++ b/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioProjectCacheHostServiceFactory.cs @@ -16,7 +16,7 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.Workspaces [Shared] internal partial class VisualStudioProjectCacheHostServiceFactory : IWorkspaceServiceFactory { - private const int ImplicitCacheTimeoutInMS = 10000; + private static readonly TimeSpan ImplicitCacheTimeout = TimeSpan.FromMilliseconds(10000); [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] @@ -42,7 +42,7 @@ private static IWorkspaceService GetMiscProjectCache(HostWorkspaceServices works return new ProjectCacheService(workspaceServices.Workspace); } - var projectCacheService = new ProjectCacheService(workspaceServices.Workspace, ImplicitCacheTimeoutInMS); + var projectCacheService = new ProjectCacheService(workspaceServices.Workspace, ImplicitCacheTimeout); // Also clear the cache when the solution is cleared or removed. workspaceServices.Workspace.WorkspaceChanged += (s, e) => @@ -59,7 +59,7 @@ private static IWorkspaceService GetMiscProjectCache(HostWorkspaceServices works private static IWorkspaceService GetVisualStudioProjectCache(HostWorkspaceServices workspaceServices) { // We will finish setting this up in VisualStudioWorkspaceImpl.DeferredInitializationState - return new ProjectCacheService(workspaceServices.Workspace, ImplicitCacheTimeoutInMS); + return new ProjectCacheService(workspaceServices.Workspace, ImplicitCacheTimeout); } internal static void ConnectProjectCacheServiceToDocumentTracking(HostWorkspaceServices workspaceServices, ProjectCacheService projectCacheService) diff --git a/src/Workspaces/Remote/Core/SolutionChecksumUpdater.cs b/src/Workspaces/Remote/Core/SolutionChecksumUpdater.cs index 0878c25167aac..447b49f1803e2 100644 --- a/src/Workspaces/Remote/Core/SolutionChecksumUpdater.cs +++ b/src/Workspaces/Remote/Core/SolutionChecksumUpdater.cs @@ -4,6 +4,7 @@ #nullable disable +using System; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Internal.Log; @@ -34,7 +35,7 @@ internal sealed class SolutionChecksumUpdater : GlobalOperationAwareIdleProcesso public SolutionChecksumUpdater(Workspace workspace, IAsynchronousOperationListenerProvider listenerProvider, CancellationToken shutdownToken) : base(listenerProvider.GetListener(FeatureAttribute.SolutionChecksumUpdater), workspace.Services.GetService(), - workspace.Options.GetOption(RemoteHostOptions.SolutionChecksumMonitorBackOffTimeSpanInMS), shutdownToken) + TimeSpan.FromMilliseconds(workspace.Options.GetOption(RemoteHostOptions.SolutionChecksumMonitorBackOffTimeSpanInMS)), shutdownToken) { _workspace = workspace; _textChangeQueue = new TaskQueue(Listener, TaskScheduler.Default); diff --git a/src/Workspaces/Remote/ServiceHub/Host/ProjectCacheHostServiceFactory.cs b/src/Workspaces/Remote/ServiceHub/Host/ProjectCacheHostServiceFactory.cs index c53e0538403c1..0d58c936114b2 100644 --- a/src/Workspaces/Remote/ServiceHub/Host/ProjectCacheHostServiceFactory.cs +++ b/src/Workspaces/Remote/ServiceHub/Host/ProjectCacheHostServiceFactory.cs @@ -13,7 +13,7 @@ namespace Microsoft.CodeAnalysis.Remote [Shared] internal partial class ProjectCacheHostServiceFactory : IWorkspaceServiceFactory { - private const int ImplicitCacheTimeoutInMS = 10000; + private static readonly TimeSpan s_implicitCacheTimeout = TimeSpan.FromMilliseconds(10000); [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] @@ -22,6 +22,6 @@ public ProjectCacheHostServiceFactory() } public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) - => new ProjectCacheService(workspaceServices.Workspace, ImplicitCacheTimeoutInMS); + => new ProjectCacheService(workspaceServices.Workspace, s_implicitCacheTimeout); } } diff --git a/src/Workspaces/Remote/ServiceHub/Services/Host/RemoteHostService.PerformanceReporter.cs b/src/Workspaces/Remote/ServiceHub/Services/Host/RemoteHostService.PerformanceReporter.cs index deaa218aec9c3..e6e77c694d198 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/Host/RemoteHostService.PerformanceReporter.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/Host/RemoteHostService.PerformanceReporter.cs @@ -43,7 +43,7 @@ public PerformanceReporter( : base( AsynchronousOperationListenerProvider.NullListener, globalOperationNotificationService, - (int)reportingInterval.TotalMilliseconds, + reportingInterval, shutdownToken) { _event = new SemaphoreSlim(initialCount: 0); From 6e2ce53655c62e4eb05209226e30382a81026d23 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Wed, 23 Jun 2021 14:14:39 -0700 Subject: [PATCH 2/5] Expedite waits in WorkCoordinatorTests --- .../SolutionCrawler/WorkCoordinatorTests.cs | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/EditorFeatures/Test/SolutionCrawler/WorkCoordinatorTests.cs b/src/EditorFeatures/Test/SolutionCrawler/WorkCoordinatorTests.cs index 14eb9c357849b..87b75b5d78cc7 100644 --- a/src/EditorFeatures/Test/SolutionCrawler/WorkCoordinatorTests.cs +++ b/src/EditorFeatures/Test/SolutionCrawler/WorkCoordinatorTests.cs @@ -788,14 +788,23 @@ internal async Task Document_Cancellation(BackgroundAnalysisScope analysisScope, var expectedDocumentSyntaxEvents = 1; var expectedDocumentSemanticEvents = 5; + var listenerProvider = GetListenerProvider(workspace.ExportProvider); + + // start an operation that allows an expedited wait to cover the remainder of the delayed operations in the test + var token = listenerProvider.GetListener(FeatureAttribute.SolutionCrawler).BeginAsyncOperation("Test operation"); + var expeditedWait = listenerProvider.GetWaiter(FeatureAttribute.SolutionCrawler).ExpeditedWaitAsync(); + workspace.ChangeDocument(document.Id, SourceText.From("//")); if (expectedDocumentSyntaxEvents > 0 || expectedDocumentSemanticEvents > 0) { analyzer.RunningEvent.Wait(); } + token.Dispose(); + workspace.ChangeDocument(document.Id, SourceText.From("// ")); await WaitAsync(service, workspace); + await expeditedWait; service.Unregister(workspace); @@ -833,6 +842,12 @@ internal async Task Document_Cancellation_MultipleTimes(BackgroundAnalysisScope service.Register(workspace); + var listenerProvider = GetListenerProvider(workspace.ExportProvider); + + // start an operation that allows an expedited wait to cover the remainder of the delayed operations in the test + var token = listenerProvider.GetListener(FeatureAttribute.SolutionCrawler).BeginAsyncOperation("Test operation"); + var expeditedWait = listenerProvider.GetWaiter(FeatureAttribute.SolutionCrawler).ExpeditedWaitAsync(); + workspace.ChangeDocument(document.Id, SourceText.From("//")); if (expectedDocumentSyntaxEvents > 0 || expectedDocumentSemanticEvents > 0) { @@ -846,8 +861,11 @@ internal async Task Document_Cancellation_MultipleTimes(BackgroundAnalysisScope analyzer.RunningEvent.Wait(); } + token.Dispose(); + workspace.ChangeDocument(document.Id, SourceText.From("// ")); await WaitAsync(service, workspace); + await expeditedWait; service.Unregister(workspace); @@ -1273,6 +1291,12 @@ public async Task FileFromSameProjectTogetherTest() await WaitWaiterAsync(workspace.ExportProvider); + var listenerProvider = GetListenerProvider(workspace.ExportProvider); + + // start an operation that allows an expedited wait to cover the remainder of the delayed operations in the test + var token = listenerProvider.GetListener(FeatureAttribute.SolutionCrawler).BeginAsyncOperation("Test operation"); + var expeditedWait = listenerProvider.GetWaiter(FeatureAttribute.SolutionCrawler).ExpeditedWaitAsync(); + // we want to test order items processed by solution crawler. // but since everything async, lazy and cancellable, order is not 100% deterministic. an item might // start to be processed, and get cancelled due to newly enqueued item requiring current work to be re-processed @@ -1319,8 +1343,11 @@ await crawlerListener.WaitUntilConditionIsMetAsync( operation.Done(); } + token.Dispose(); + // wait analyzers to finish process await WaitAsync(service, workspace); + await expeditedWait; Assert.Equal(1, worker.DocumentIds.Take(5).Select(d => d.ProjectId).Distinct().Count()); Assert.Equal(1, worker.DocumentIds.Skip(5).Take(5).Select(d => d.ProjectId).Distinct().Count()); From 089ff931895f76a324e839296115d222f07869c9 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Wed, 23 Jun 2021 14:32:59 -0700 Subject: [PATCH 3/5] Remove unnecessary configurable delays --- ...eviewSolutionCrawlerRegistrationService.cs | 4 ++-- .../SolutionCrawler/WorkCoordinatorTests.cs | 15 ------------ .../InternalSolutionCrawlerOptions.cs | 24 ++++++------------- .../InternalSolutionCrawlerOptionsProvider.cs | 7 +----- .../SolutionCrawler/WorkCoordinator.cs | 10 ++++---- .../Impl/CodeModel/ProjectCodeModelFactory.cs | 5 ++-- 6 files changed, 17 insertions(+), 48 deletions(-) diff --git a/src/EditorFeatures/Core/Shared/Preview/PreviewSolutionCrawlerRegistrationService.cs b/src/EditorFeatures/Core/Shared/Preview/PreviewSolutionCrawlerRegistrationService.cs index 0b5cf858ab7e5..a198812083642 100644 --- a/src/EditorFeatures/Core/Shared/Preview/PreviewSolutionCrawlerRegistrationService.cs +++ b/src/EditorFeatures/Core/Shared/Preview/PreviewSolutionCrawlerRegistrationService.cs @@ -72,7 +72,7 @@ public void Register(Workspace workspace) private async Task AnalyzeAsync() { - var workerBackOffTimeSpanInMS = _workspace.Options.GetOption(InternalSolutionCrawlerOptions.PreviewBackOffTimeSpanInMS); + var workerBackOffTimeSpan = InternalSolutionCrawlerOptions.PreviewBackOffTimeSpan; var incrementalAnalyzer = _owner._analyzerService.CreateIncrementalAnalyzer(_workspace); var solution = _workspace.CurrentSolution; @@ -89,7 +89,7 @@ private async Task AnalyzeAsync() } // delay analyzing - await Task.Delay(workerBackOffTimeSpanInMS, _source.Token).ConfigureAwait(false); + await Task.Delay(workerBackOffTimeSpan, _source.Token).ConfigureAwait(false); // do actual analysis if (textDocument is Document document) diff --git a/src/EditorFeatures/Test/SolutionCrawler/WorkCoordinatorTests.cs b/src/EditorFeatures/Test/SolutionCrawler/WorkCoordinatorTests.cs index 87b75b5d78cc7..3ecd69d10d544 100644 --- a/src/EditorFeatures/Test/SolutionCrawler/WorkCoordinatorTests.cs +++ b/src/EditorFeatures/Test/SolutionCrawler/WorkCoordinatorTests.cs @@ -1366,7 +1366,6 @@ private static async Task InsertText(string code, string text, bool expectDocume composition: EditorTestCompositions.EditorFeatures.AddExcludedPartTypes(typeof(IIncrementalAnalyzerProvider)).AddParts(typeof(AnalyzerProviderNoWaitNoBlock)), workspaceKind: SolutionCrawlerWorkspaceKind); - SetOptions(workspace); var testDocument = workspace.Documents.First(); var textBuffer = testDocument.GetTextBuffer(); @@ -1503,18 +1502,6 @@ private static IEnumerable GetDocuments(ProjectId projectId, int c private static AsynchronousOperationListenerProvider GetListenerProvider(ExportProvider provider) => provider.GetExportedValue(); - private static void SetOptions(Workspace workspace) - { - // override default timespan to make test run faster - workspace.TryApplyChanges(workspace.CurrentSolution.WithOptions(workspace.Options - .WithChangedOption(InternalSolutionCrawlerOptions.ActiveFileWorkerBackOffTimeSpanInMS, 0) - .WithChangedOption(InternalSolutionCrawlerOptions.AllFilesWorkerBackOffTimeSpanInMS, 0) - .WithChangedOption(InternalSolutionCrawlerOptions.PreviewBackOffTimeSpanInMS, 0) - .WithChangedOption(InternalSolutionCrawlerOptions.ProjectPropagationBackOffTimeSpanInMS, 0) - .WithChangedOption(InternalSolutionCrawlerOptions.SemanticChangeBackOffTimeSpanInMS, 0) - .WithChangedOption(InternalSolutionCrawlerOptions.EntireProjectWorkerBackOffTimeSpanInMS, 100))); - } - private static void MakeFirstDocumentActive(Project project) => MakeDocumentActive(project.Documents.First()); @@ -1545,8 +1532,6 @@ public WorkCoordinatorWorkspace(string workspaceKind = null, bool disablePartial Assert.False(_workspaceWaiter.HasPendingWork); Assert.False(_solutionCrawlerWaiter.HasPendingWork); - - WorkCoordinatorTests.SetOptions(this); } public static WorkCoordinatorWorkspace CreateWithAnalysisScope(BackgroundAnalysisScope analysisScope, string workspaceKind = null, bool disablePartialSolutions = true, Type incrementalAnalyzer = null) diff --git a/src/Features/Core/Portable/SolutionCrawler/InternalSolutionCrawlerOptions.cs b/src/Features/Core/Portable/SolutionCrawler/InternalSolutionCrawlerOptions.cs index b6eb290a7754d..58529ebc46875 100644 --- a/src/Features/Core/Portable/SolutionCrawler/InternalSolutionCrawlerOptions.cs +++ b/src/Features/Core/Portable/SolutionCrawler/InternalSolutionCrawlerOptions.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using Microsoft.CodeAnalysis.Options; namespace Microsoft.CodeAnalysis.SolutionCrawler @@ -16,22 +17,11 @@ internal static class InternalSolutionCrawlerOptions public static readonly Option2 DirectDependencyPropagationOnly = new(nameof(InternalSolutionCrawlerOptions), "Project propagation only on direct dependency", defaultValue: true, storageLocations: new LocalUserProfileStorageLocation(LocalRegistryPath + "Project propagation only on direct dependency")); - public static readonly Option2 ActiveFileWorkerBackOffTimeSpanInMS = new(nameof(InternalSolutionCrawlerOptions), "Active file worker backoff timespan in ms", defaultValue: 100, - storageLocations: new LocalUserProfileStorageLocation(LocalRegistryPath + "Active file worker backoff timespan in ms")); - - public static readonly Option2 AllFilesWorkerBackOffTimeSpanInMS = new(nameof(InternalSolutionCrawlerOptions), "All files worker backoff timespan in ms", defaultValue: 1500, - storageLocations: new LocalUserProfileStorageLocation(LocalRegistryPath + "All files worker backoff timespan in ms")); - - public static readonly Option2 EntireProjectWorkerBackOffTimeSpanInMS = new(nameof(InternalSolutionCrawlerOptions), "Entire project analysis worker backoff timespan in ms", defaultValue: 5000, - storageLocations: new LocalUserProfileStorageLocation(LocalRegistryPath + "Entire project analysis worker backoff timespan in ms")); - - public static readonly Option2 SemanticChangeBackOffTimeSpanInMS = new(nameof(InternalSolutionCrawlerOptions), "Semantic change backoff timespan in ms", defaultValue: 100, - storageLocations: new LocalUserProfileStorageLocation(LocalRegistryPath + "Semantic change backoff timespan in ms")); - - public static readonly Option2 ProjectPropagationBackOffTimeSpanInMS = new(nameof(InternalSolutionCrawlerOptions), "Project propagation backoff timespan in ms", defaultValue: 500, - storageLocations: new LocalUserProfileStorageLocation(LocalRegistryPath + "Project propagation backoff timespan in ms")); - - public static readonly Option2 PreviewBackOffTimeSpanInMS = new(nameof(InternalSolutionCrawlerOptions), "Preview backoff timespan in ms", defaultValue: 500, - storageLocations: new LocalUserProfileStorageLocation(LocalRegistryPath + "Preview backoff timespan in ms")); + public static readonly TimeSpan ActiveFileWorkerBackOffTimeSpan = TimeSpan.FromMilliseconds(100); + public static readonly TimeSpan AllFilesWorkerBackOffTimeSpan = TimeSpan.FromMilliseconds(1500); + public static readonly TimeSpan EntireProjectWorkerBackOffTimeSpan = TimeSpan.FromMilliseconds(5000); + public static readonly TimeSpan SemanticChangeBackOffTimeSpan = TimeSpan.FromMilliseconds(100); + public static readonly TimeSpan ProjectPropagationBackOffTimeSpan = TimeSpan.FromMilliseconds(500); + public static readonly TimeSpan PreviewBackOffTimeSpan = TimeSpan.FromMilliseconds(500); } } diff --git a/src/Features/Core/Portable/SolutionCrawler/InternalSolutionCrawlerOptionsProvider.cs b/src/Features/Core/Portable/SolutionCrawler/InternalSolutionCrawlerOptionsProvider.cs index 37ae86b2c73bc..2a5e8c14adad1 100644 --- a/src/Features/Core/Portable/SolutionCrawler/InternalSolutionCrawlerOptionsProvider.cs +++ b/src/Features/Core/Portable/SolutionCrawler/InternalSolutionCrawlerOptionsProvider.cs @@ -22,11 +22,6 @@ public InternalSolutionCrawlerOptionsProvider() public ImmutableArray Options { get; } = ImmutableArray.Create( InternalSolutionCrawlerOptions.SolutionCrawler, - InternalSolutionCrawlerOptions.ActiveFileWorkerBackOffTimeSpanInMS, - InternalSolutionCrawlerOptions.AllFilesWorkerBackOffTimeSpanInMS, - InternalSolutionCrawlerOptions.EntireProjectWorkerBackOffTimeSpanInMS, - InternalSolutionCrawlerOptions.SemanticChangeBackOffTimeSpanInMS, - InternalSolutionCrawlerOptions.ProjectPropagationBackOffTimeSpanInMS, - InternalSolutionCrawlerOptions.PreviewBackOffTimeSpanInMS); + InternalSolutionCrawlerOptions.DirectDependencyPropagationOnly); } } diff --git a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.cs b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.cs index 51bdc2490fb4e..430d5e1f23180 100644 --- a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.cs +++ b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.cs @@ -57,16 +57,16 @@ public WorkCoordinator( _eventProcessingQueue = new TaskQueue(listener, TaskScheduler.Default); - var activeFileBackOffTimeSpan = TimeSpan.FromMilliseconds(_optionService.GetOption(InternalSolutionCrawlerOptions.ActiveFileWorkerBackOffTimeSpanInMS)); - var allFilesWorkerBackOffTimeSpan = TimeSpan.FromMilliseconds(_optionService.GetOption(InternalSolutionCrawlerOptions.AllFilesWorkerBackOffTimeSpanInMS)); - var entireProjectWorkerBackOffTimeSpan = TimeSpan.FromMilliseconds(_optionService.GetOption(InternalSolutionCrawlerOptions.EntireProjectWorkerBackOffTimeSpanInMS)); + var activeFileBackOffTimeSpan = InternalSolutionCrawlerOptions.ActiveFileWorkerBackOffTimeSpan; + var allFilesWorkerBackOffTimeSpan = InternalSolutionCrawlerOptions.AllFilesWorkerBackOffTimeSpan; + var entireProjectWorkerBackOffTimeSpan = InternalSolutionCrawlerOptions.EntireProjectWorkerBackOffTimeSpan; _documentAndProjectWorkerProcessor = new IncrementalAnalyzerProcessor( listener, analyzerProviders, initializeLazily, _registration, activeFileBackOffTimeSpan, allFilesWorkerBackOffTimeSpan, entireProjectWorkerBackOffTimeSpan, _shutdownToken); - var semanticBackOffTimeSpan = TimeSpan.FromMilliseconds(_optionService.GetOption(InternalSolutionCrawlerOptions.SemanticChangeBackOffTimeSpanInMS)); - var projectBackOffTimeSpan = TimeSpan.FromMilliseconds(_optionService.GetOption(InternalSolutionCrawlerOptions.ProjectPropagationBackOffTimeSpanInMS)); + var semanticBackOffTimeSpan = InternalSolutionCrawlerOptions.SemanticChangeBackOffTimeSpan; + var projectBackOffTimeSpan = InternalSolutionCrawlerOptions.ProjectPropagationBackOffTimeSpan; _semanticChangeProcessor = new SemanticChangeProcessor(listener, _registration, _documentAndProjectWorkerProcessor, semanticBackOffTimeSpan, projectBackOffTimeSpan, _shutdownToken); diff --git a/src/VisualStudio/Core/Impl/CodeModel/ProjectCodeModelFactory.cs b/src/VisualStudio/Core/Impl/CodeModel/ProjectCodeModelFactory.cs index 77abdc8dd3ea8..06a6f7fc6a383 100644 --- a/src/VisualStudio/Core/Impl/CodeModel/ProjectCodeModelFactory.cs +++ b/src/VisualStudio/Core/Impl/CodeModel/ProjectCodeModelFactory.cs @@ -16,6 +16,7 @@ using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.LanguageServices; using Microsoft.CodeAnalysis.Shared.TestHooks; +using Microsoft.CodeAnalysis.SolutionCrawler; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Threading; using Roslyn.Utilities; @@ -27,8 +28,6 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel [Export(typeof(ProjectCodeModelFactory))] internal sealed class ProjectCodeModelFactory : ForegroundThreadAffinitizedObject, IProjectCodeModelFactory { - private static readonly TimeSpan s_documentBatchProcessingCadence = TimeSpan.FromMilliseconds(1500); - private readonly ConcurrentDictionary _projectCodeModels = new ConcurrentDictionary(); private readonly VisualStudioWorkspace _visualStudioWorkspace; @@ -57,7 +56,7 @@ public ProjectCodeModelFactory( // for the same documents. Once enough time has passed, take the documents that were changed and run // through them, firing their latest events. _documentsToFireEventsFor = new AsyncBatchingWorkQueue( - s_documentBatchProcessingCadence, + InternalSolutionCrawlerOptions.AllFilesWorkerBackOffTimeSpan, ProcessNextDocumentBatchAsync, // We only care about unique doc-ids, so pass in this comparer to collapse streams of changes for a // single document down to one notification. From df89ea01446e0c8bdbef80c5ea091ea14342b023 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Wed, 23 Jun 2021 14:35:33 -0700 Subject: [PATCH 4/5] Use IExpeditableDelaySource for delay in PreviewSolutionCrawlerRegistrationServiceFactory --- .../Shared/Preview/PreviewSolutionCrawlerRegistrationService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EditorFeatures/Core/Shared/Preview/PreviewSolutionCrawlerRegistrationService.cs b/src/EditorFeatures/Core/Shared/Preview/PreviewSolutionCrawlerRegistrationService.cs index a198812083642..ec124733a96ec 100644 --- a/src/EditorFeatures/Core/Shared/Preview/PreviewSolutionCrawlerRegistrationService.cs +++ b/src/EditorFeatures/Core/Shared/Preview/PreviewSolutionCrawlerRegistrationService.cs @@ -89,7 +89,7 @@ private async Task AnalyzeAsync() } // delay analyzing - await Task.Delay(workerBackOffTimeSpan, _source.Token).ConfigureAwait(false); + await _owner._listener.Delay(workerBackOffTimeSpan, _source.Token).ConfigureAwait(false); // do actual analysis if (textDocument is Document document) From b4f21dacb50ef469ab53d2365e20d3f7b89039a4 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Wed, 23 Jun 2021 14:37:16 -0700 Subject: [PATCH 5/5] Yield instead of delay on expedited wait completion in IdleProcessor --- .../Core/Portable/SolutionCrawler/IdleProcessor.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Features/Core/Portable/SolutionCrawler/IdleProcessor.cs b/src/Features/Core/Portable/SolutionCrawler/IdleProcessor.cs index ff96d9124b6ae..84618890b7bef 100644 --- a/src/Features/Core/Portable/SolutionCrawler/IdleProcessor.cs +++ b/src/Features/Core/Portable/SolutionCrawler/IdleProcessor.cs @@ -67,12 +67,12 @@ protected async Task WaitForIdleAsync(IExpeditableDelaySource expeditableDelaySo var timeLeft = BackOffTimeSpan - diff; if (!await expeditableDelaySource.Delay(TimeSpan.FromMilliseconds(Math.Max(s_minimumDelay.TotalMilliseconds, timeLeft.TotalMilliseconds)), CancellationToken).ConfigureAwait(false)) { - // The delay terminated early to accommodate a blocking operation. Make sure to delay long - // enough that low priority (on idle) operations get a chance to be triggered. + // The delay terminated early to accommodate a blocking operation. Make sure to yield so low + // priority (on idle) operations get a chance to be triggered. // - // 📝 At the time this was discovered, it was not clear exactly why the delay was needed in order - // to avoid live-lock scenarios. - await Task.Delay(TimeSpan.FromMilliseconds(10), CancellationToken).ConfigureAwait(false); + // 📝 At the time this was discovered, it was not clear exactly why the yield (previously delay) + // was needed in order to avoid live-lock scenarios. + await Task.Yield().ConfigureAwait(false); return; } }