From 3b8b35ae4664a6952d52d4030fd21b811de67324 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 21 Apr 2021 12:57:20 -0700 Subject: [PATCH 01/13] Remove usages of ForegroundNotificationService in favor of JTF. --- .../InfoBar/VisualStudioInfoBarService.cs | 10 +++------- .../VisualStudioRuleSetManager.RuleSetFile.cs | 9 +++++++-- .../RuleSets/VisualStudioRuleSetManager.cs | 10 ++++++---- .../VisualStudioRuleSetManagerFactory.cs | 16 +++++----------- .../VisualStudioRuleSetTests.vb | 13 +++++++------ 5 files changed, 28 insertions(+), 30 deletions(-) diff --git a/src/VisualStudio/Core/Def/Implementation/InfoBar/VisualStudioInfoBarService.cs b/src/VisualStudio/Core/Def/Implementation/InfoBar/VisualStudioInfoBarService.cs index ac21ff0268e4a..5b2f80ae78d9c 100644 --- a/src/VisualStudio/Core/Def/Implementation/InfoBar/VisualStudioInfoBarService.cs +++ b/src/VisualStudio/Core/Def/Implementation/InfoBar/VisualStudioInfoBarService.cs @@ -24,7 +24,6 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation internal class VisualStudioInfoBarService : ForegroundThreadAffinitizedObject, IInfoBarService { private readonly SVsServiceProvider _serviceProvider; - private readonly IForegroundNotificationService _foregroundNotificationService; private readonly IAsynchronousOperationListener _listener; [ImportingConstructor] @@ -32,12 +31,10 @@ internal class VisualStudioInfoBarService : ForegroundThreadAffinitizedObject, I public VisualStudioInfoBarService( IThreadingContext threadingContext, SVsServiceProvider serviceProvider, - IForegroundNotificationService foregroundNotificationService, IAsynchronousOperationListenerProvider listenerProvider) : base(threadingContext) { _serviceProvider = serviceProvider; - _foregroundNotificationService = foregroundNotificationService; _listener = listenerProvider.GetListener(FeatureAttribute.InfoBar); } @@ -46,13 +43,12 @@ public void ShowInfoBar(string message, params InfoBarUI[] items) ThisCanBeCalledOnAnyThread(); // We can be called from any thread since errors can occur anywhere, however we can only construct and InfoBar from the UI thread. - _foregroundNotificationService.RegisterNotification(() => + System.Threading.Tasks.Task.Run(async () => { + await this.ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(ThreadingContext.DisposalToken); if (TryGetInfoBarData(out var infoBarHost)) - { CreateInfoBar(infoBarHost, message, items); - } - }, _listener.BeginAsyncOperation(nameof(ShowInfoBar)), ThreadingContext.DisposalToken); + }, ThreadingContext.DisposalToken).CompletesAsyncOperation(_listener.BeginAsyncOperation(nameof(ShowInfoBar))); } private bool TryGetInfoBarData(out IVsInfoBarHost infoBarHost) diff --git a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/RuleSets/VisualStudioRuleSetManager.RuleSetFile.cs b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/RuleSets/VisualStudioRuleSetManager.RuleSetFile.cs index c33167f72bcb3..cf46ff79825fd 100644 --- a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/RuleSets/VisualStudioRuleSetManager.RuleSetFile.cs +++ b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/RuleSets/VisualStudioRuleSetManager.RuleSetFile.cs @@ -8,7 +8,9 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Threading; +using System.Threading.Tasks; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Shared.TestHooks; namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem { @@ -177,8 +179,11 @@ private void IncludeUpdated(object sender, string fileChanged) // waiting for the foreground thread to release its lock on the file change service. // To avoid this, just queue up a Task to do the work on the foreground thread later, after // the lock on the file change service has been released. - _ruleSetManager._foregroundNotificationService.RegisterNotification( - () => IncludeUpdateCore(), _ruleSetManager._listener.BeginAsyncOperation("IncludeUpdated"), _disposalToken); + Task.Run(async () => + { + await _ruleSetManager._threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(_disposalToken); + IncludeUpdateCore(); + }, _disposalToken).CompletesAsyncOperation(_ruleSetManager._listener.BeginAsyncOperation("IncludeUpdated")); } private void IncludeUpdateCore() diff --git a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/RuleSets/VisualStudioRuleSetManager.cs b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/RuleSets/VisualStudioRuleSetManager.cs index a0734e94a6fa2..e556902faaed3 100644 --- a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/RuleSets/VisualStudioRuleSetManager.cs +++ b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/RuleSets/VisualStudioRuleSetManager.cs @@ -4,7 +4,7 @@ #nullable disable -using Microsoft.CodeAnalysis.Editor; +using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Shared.TestHooks; using Roslyn.Utilities; @@ -13,17 +13,19 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem { internal sealed partial class VisualStudioRuleSetManager : IWorkspaceService { + private readonly IThreadingContext _threadingContext; private readonly FileChangeWatcher _fileChangeWatcher; - private readonly IForegroundNotificationService _foregroundNotificationService; private readonly IAsynchronousOperationListener _listener; private readonly ReferenceCountedDisposableCache _ruleSetFileMap = new(); public VisualStudioRuleSetManager( - FileChangeWatcher fileChangeWatcher, IForegroundNotificationService foregroundNotificationService, IAsynchronousOperationListener listener) + IThreadingContext threadingContext, + FileChangeWatcher fileChangeWatcher, + IAsynchronousOperationListener listener) { + _threadingContext = threadingContext; _fileChangeWatcher = fileChangeWatcher; - _foregroundNotificationService = foregroundNotificationService; _listener = listener; } diff --git a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/RuleSets/VisualStudioRuleSetManagerFactory.cs b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/RuleSets/VisualStudioRuleSetManagerFactory.cs index f218e47ec7bc3..b12dda642a839 100644 --- a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/RuleSets/VisualStudioRuleSetManagerFactory.cs +++ b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/RuleSets/VisualStudioRuleSetManagerFactory.cs @@ -2,41 +2,35 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System; -using System.Collections.Generic; using System.Composition; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Editor; +using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Shared.TestHooks; -using Microsoft.VisualStudio.Shell; -using Microsoft.VisualStudio.Shell.Interop; namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem { [ExportWorkspaceServiceFactory(typeof(VisualStudioRuleSetManager), ServiceLayer.Host), Shared] internal sealed class VisualStudioRuleSetManagerFactory : IWorkspaceServiceFactory { + private readonly IThreadingContext _threadingContext; private readonly FileChangeWatcherProvider _fileChangeWatcherProvider; - private readonly IForegroundNotificationService _foregroundNotificationService; private readonly IAsynchronousOperationListener _listener; [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] public VisualStudioRuleSetManagerFactory( + IThreadingContext threadingContext, FileChangeWatcherProvider fileChangeWatcherProvider, - IForegroundNotificationService foregroundNotificationService, IAsynchronousOperationListenerProvider listenerProvider) { + _threadingContext = threadingContext; _fileChangeWatcherProvider = fileChangeWatcherProvider; - _foregroundNotificationService = foregroundNotificationService; _listener = listenerProvider.GetListener(FeatureAttribute.RuleSetEditor); } public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) - => new VisualStudioRuleSetManager(_fileChangeWatcherProvider.Watcher, _foregroundNotificationService, _listener); + => new VisualStudioRuleSetManager(_threadingContext, _fileChangeWatcherProvider.Watcher, _listener); } } diff --git a/src/VisualStudio/Core/Test/ProjectSystemShim/VisualStudioRuleSetTests.vb b/src/VisualStudio/Core/Test/ProjectSystemShim/VisualStudioRuleSetTests.vb index 80fe5af3d256d..fafb1ab5b1548 100644 --- a/src/VisualStudio/Core/Test/ProjectSystemShim/VisualStudioRuleSetTests.vb +++ b/src/VisualStudio/Core/Test/ProjectSystemShim/VisualStudioRuleSetTests.vb @@ -5,6 +5,7 @@ Imports System.IO Imports Microsoft.CodeAnalysis Imports Microsoft.CodeAnalysis.Editor +Imports Microsoft.CodeAnalysis.Editor.Shared.Utilities Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces Imports Microsoft.CodeAnalysis.Shared.TestHooks Imports Microsoft.CodeAnalysis.Test.Utilities @@ -48,7 +49,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.ProjectSystemShim Dim fileChangeService = New MockVsFileChangeEx Dim fileChangeWatcher = New FileChangeWatcher(Task.FromResult(Of IVsAsyncFileChangeEx)(fileChangeService)) - Dim ruleSetManager = New VisualStudioRuleSetManager(fileChangeWatcher, workspace.ExportProvider.GetExportedValue(Of IForegroundNotificationService)(), AsynchronousOperationListenerProvider.NullListener) + Dim ruleSetManager = New VisualStudioRuleSetManager(workspace.ExportProvider.GetExportedValue(Of IThreadingContext), fileChangeWatcher, AsynchronousOperationListenerProvider.NullListener) Using visualStudioRuleSet = ruleSetManager.GetOrCreateRuleSet(ruleSetPath) ' Signing up for file change notifications is lazy, so read the rule set to force it. @@ -92,7 +93,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.ProjectSystemShim Using workspace = New TestWorkspace() Dim fileChangeService = New MockVsFileChangeEx Dim fileChangeWatcher = New FileChangeWatcher(Task.FromResult(Of IVsAsyncFileChangeEx)(fileChangeService)) - Dim ruleSetManager = New VisualStudioRuleSetManager(fileChangeWatcher, workspace.ExportProvider.GetExportedValue(Of IForegroundNotificationService)(), AsynchronousOperationListenerProvider.NullListener) + Dim ruleSetManager = New VisualStudioRuleSetManager(workspace.ExportProvider.GetExportedValue(Of IThreadingContext), fileChangeWatcher, AsynchronousOperationListenerProvider.NullListener) Using visualStudioRuleSet = ruleSetManager.GetOrCreateRuleSet(ruleSetPath) ' Signing up for file change notifications is lazy, so read the rule set to force it. @@ -140,7 +141,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.ProjectSystemShim Dim listenerProvider = workspace.ExportProvider.GetExportedValue(Of IAsynchronousOperationListenerProvider) Dim listener = listenerProvider.GetListener("test") - Dim ruleSetManager = New VisualStudioRuleSetManager(fileChangeWatcher, workspace.ExportProvider.GetExportedValue(Of IForegroundNotificationService)(), listener) + Dim ruleSetManager = New VisualStudioRuleSetManager(workspace.ExportProvider.GetExportedValue(Of IThreadingContext), fileChangeWatcher, listener) Using ruleSet1 = ruleSetManager.GetOrCreateRuleSet(ruleSetPath) Dim handlerCalled As Boolean = False AddHandler ruleSet1.Target.Value.UpdatedOnDisk, Sub() handlerCalled = True @@ -181,7 +182,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.ProjectSystemShim Dim listenerProvider = workspace.ExportProvider.GetExportedValue(Of IAsynchronousOperationListenerProvider) Dim listener = listenerProvider.GetListener("test") - Dim ruleSetManager = New VisualStudioRuleSetManager(fileChangeWatcher, workspace.ExportProvider.GetExportedValue(Of IForegroundNotificationService)(), listener) + Dim ruleSetManager = New VisualStudioRuleSetManager(workspace.ExportProvider.GetExportedValue(Of IThreadingContext), fileChangeWatcher, listener) Using ruleSet1 = ruleSetManager.GetOrCreateRuleSet(ruleSetPath) ' Signing up for file change notifications is lazy, so read the rule set to force it. @@ -226,7 +227,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.ProjectSystemShim Using workspace = New TestWorkspace() Dim fileChangeService = New MockVsFileChangeEx Dim fileChangeWatcher = New FileChangeWatcher(Task.FromResult(Of IVsAsyncFileChangeEx)(fileChangeService)) - Dim ruleSetManager = New VisualStudioRuleSetManager(fileChangeWatcher, workspace.ExportProvider.GetExportedValue(Of IForegroundNotificationService)(), AsynchronousOperationListenerProvider.NullListener) + Dim ruleSetManager = New VisualStudioRuleSetManager(workspace.ExportProvider.GetExportedValue(Of IThreadingContext), fileChangeWatcher, AsynchronousOperationListenerProvider.NullListener) Using ruleSet1 = ruleSetManager.GetOrCreateRuleSet(ruleSetPath) ' Signing up for file change notifications is lazy, so read the rule set to force it. @@ -264,7 +265,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.ProjectSystemShim Using workspace = New TestWorkspace() Dim fileChangeService = New MockVsFileChangeEx Dim fileChangeWatcher = New FileChangeWatcher(Task.FromResult(Of IVsAsyncFileChangeEx)(fileChangeService)) - Dim ruleSetManager = New VisualStudioRuleSetManager(fileChangeWatcher, workspace.ExportProvider.GetExportedValue(Of IForegroundNotificationService)(), AsynchronousOperationListenerProvider.NullListener) + Dim ruleSetManager = New VisualStudioRuleSetManager(workspace.ExportProvider.GetExportedValue(Of IThreadingContext), fileChangeWatcher, AsynchronousOperationListenerProvider.NullListener) Using ruleSet = ruleSetManager.GetOrCreateRuleSet(ruleSetPath) Dim generalDiagnosticOption = ruleSet.Target.Value.GetGeneralDiagnosticOption() From 5e065d04d3212eda1202ae1644436d9be0a7c02c Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 21 Apr 2021 13:02:49 -0700 Subject: [PATCH 02/13] Remove more usages --- .../Test/CodeModel/FileCodeModelTestHelpers.cs | 3 --- .../Core/Impl/CodeModel/ProjectCodeModelFactory.cs | 14 ++++++-------- .../CodeModel/CodeModelTestHelpers.vb | 4 ---- 3 files changed, 6 insertions(+), 15 deletions(-) diff --git a/src/VisualStudio/CSharp/Test/CodeModel/FileCodeModelTestHelpers.cs b/src/VisualStudio/CSharp/Test/CodeModel/FileCodeModelTestHelpers.cs index 9882123bc1152..e3d9b1fc23244 100644 --- a/src/VisualStudio/CSharp/Test/CodeModel/FileCodeModelTestHelpers.cs +++ b/src/VisualStudio/CSharp/Test/CodeModel/FileCodeModelTestHelpers.cs @@ -6,7 +6,6 @@ using System.Linq; using System.Runtime.ExceptionServices; -using Microsoft.CodeAnalysis.Editor; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; using Microsoft.CodeAnalysis.Shared.TestHooks; @@ -41,7 +40,6 @@ public static (TestWorkspace workspace, VisualStudioWorkspace extraWorkspaceToDi var visualStudioWorkspaceMock = new MockVisualStudioWorkspace(workspace); var threadingContext = workspace.ExportProvider.GetExportedValue(); - var notificationService = workspace.ExportProvider.GetExportedValue(); var listenerProvider = workspace.ExportProvider.GetExportedValue(); var state = new CodeModelState( @@ -53,7 +51,6 @@ public static (TestWorkspace workspace, VisualStudioWorkspace extraWorkspaceToDi visualStudioWorkspaceMock, serviceProvider, threadingContext, - notificationService, listenerProvider)); var codeModel = FileCodeModel.Create(state, null, document, new MockTextManagerAdapter()).Handle; diff --git a/src/VisualStudio/Core/Impl/CodeModel/ProjectCodeModelFactory.cs b/src/VisualStudio/Core/Impl/CodeModel/ProjectCodeModelFactory.cs index f6f79adc207ee..38b4cbf913c6a 100644 --- a/src/VisualStudio/Core/Impl/CodeModel/ProjectCodeModelFactory.cs +++ b/src/VisualStudio/Core/Impl/CodeModel/ProjectCodeModelFactory.cs @@ -31,7 +31,6 @@ internal sealed class ProjectCodeModelFactory : IProjectCodeModelFactory private readonly IThreadingContext _threadingContext; - private readonly IForegroundNotificationService _notificationService; private readonly IAsynchronousOperationListener _listener; private readonly AsyncBatchingWorkQueue _documentsToFireEventsFor; @@ -41,14 +40,12 @@ public ProjectCodeModelFactory( VisualStudioWorkspace visualStudioWorkspace, [Import(typeof(SVsServiceProvider))] IServiceProvider serviceProvider, IThreadingContext threadingContext, - IForegroundNotificationService notificationService, IAsynchronousOperationListenerProvider listenerProvider) { _visualStudioWorkspace = visualStudioWorkspace; _serviceProvider = serviceProvider; _threadingContext = threadingContext; - _notificationService = notificationService; _listener = listenerProvider.GetListener(FeatureAttribute.CodeModel); // Queue up notifications we hear about docs changing. that way we don't have to fire events multiple times @@ -66,7 +63,7 @@ public ProjectCodeModelFactory( _visualStudioWorkspace.WorkspaceChanged += OnWorkspaceChanged; } - private System.Threading.Tasks.Task ProcessNextDocumentBatchAsync( + private Task ProcessNextDocumentBatchAsync( ImmutableArray documentIds, CancellationToken cancellationToken) { foreach (var documentId in documentIds) @@ -74,10 +71,11 @@ private System.Threading.Tasks.Task ProcessNextDocumentBatchAsync( // Now, enqueue foreground work to actually process these documents in a serialized and incremental // fashion. FireEventsForDocument will actually limit how much time it spends firing events so that it // doesn't saturate the UI thread. - _notificationService.RegisterNotification( - () => FireEventsForDocument(documentId), - _listener.BeginAsyncOperation("CodeModelEvent"), - cancellationToken); + Task.Run(async () => + { + await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + FireEventsForDocument(documentId); + }, cancellationToken).CompletesAsyncOperation(_listener.BeginAsyncOperation("CodeModelEvent")); } return System.Threading.Tasks.Task.CompletedTask; diff --git a/src/VisualStudio/TestUtilities2/CodeModel/CodeModelTestHelpers.vb b/src/VisualStudio/TestUtilities2/CodeModel/CodeModelTestHelpers.vb index 96c66d32b7186..076bf061de3d8 100644 --- a/src/VisualStudio/TestUtilities2/CodeModel/CodeModelTestHelpers.vb +++ b/src/VisualStudio/TestUtilities2/CodeModel/CodeModelTestHelpers.vb @@ -6,9 +6,7 @@ Imports System.Runtime.CompilerServices Imports System.Runtime.ExceptionServices Imports System.Runtime.InteropServices Imports EnvDTE -Imports EnvDTE80 Imports Microsoft.CodeAnalysis -Imports Microsoft.CodeAnalysis.Editor Imports Microsoft.CodeAnalysis.Editor.Shared.Utilities Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces Imports Microsoft.CodeAnalysis.Shared.TestHooks @@ -58,7 +56,6 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.CodeModel Dim project = workspace.CurrentSolution.Projects.Single() Dim threadingContext = workspace.ExportProvider.GetExportedValue(Of IThreadingContext) - Dim notificationService = workspace.ExportProvider.GetExportedValue(Of IForegroundNotificationService) Dim listenerProvider = workspace.ExportProvider.GetExportedValue(Of AsynchronousOperationListenerProvider)() Dim state = New CodeModelState( @@ -70,7 +67,6 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.CodeModel mockVisualStudioWorkspace, mockServiceProvider, threadingContext, - notificationService, listenerProvider)) Dim projectCodeModel = DirectCast(state.ProjectCodeModelFactory.CreateProjectCodeModel(project.Id, Nothing), ProjectCodeModel) From 890dcb3de6113927a5ced0ee7683ee7cb782e621 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 21 Apr 2021 14:11:21 -0700 Subject: [PATCH 03/13] pr feedback --- .../Def/Implementation/InfoBar/VisualStudioInfoBarService.cs | 5 +++-- .../RuleSets/VisualStudioRuleSetManager.RuleSetFile.cs | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/VisualStudio/Core/Def/Implementation/InfoBar/VisualStudioInfoBarService.cs b/src/VisualStudio/Core/Def/Implementation/InfoBar/VisualStudioInfoBarService.cs index 5b2f80ae78d9c..35cd555a99e19 100644 --- a/src/VisualStudio/Core/Def/Implementation/InfoBar/VisualStudioInfoBarService.cs +++ b/src/VisualStudio/Core/Def/Implementation/InfoBar/VisualStudioInfoBarService.cs @@ -43,12 +43,13 @@ public void ShowInfoBar(string message, params InfoBarUI[] items) ThisCanBeCalledOnAnyThread(); // We can be called from any thread since errors can occur anywhere, however we can only construct and InfoBar from the UI thread. - System.Threading.Tasks.Task.Run(async () => + this.ThreadingContext.JoinableTaskFactory.RunAsync(async () => { + using var _ = _listener.BeginAsyncOperation(nameof(ShowInfoBar)); await this.ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(ThreadingContext.DisposalToken); if (TryGetInfoBarData(out var infoBarHost)) CreateInfoBar(infoBarHost, message, items); - }, ThreadingContext.DisposalToken).CompletesAsyncOperation(_listener.BeginAsyncOperation(nameof(ShowInfoBar))); + }); } private bool TryGetInfoBarData(out IVsInfoBarHost infoBarHost) diff --git a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/RuleSets/VisualStudioRuleSetManager.RuleSetFile.cs b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/RuleSets/VisualStudioRuleSetManager.RuleSetFile.cs index cf46ff79825fd..da52974359c9f 100644 --- a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/RuleSets/VisualStudioRuleSetManager.RuleSetFile.cs +++ b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/RuleSets/VisualStudioRuleSetManager.RuleSetFile.cs @@ -179,11 +179,12 @@ private void IncludeUpdated(object sender, string fileChanged) // waiting for the foreground thread to release its lock on the file change service. // To avoid this, just queue up a Task to do the work on the foreground thread later, after // the lock on the file change service has been released. - Task.Run(async () => + _ruleSetManager._threadingContext.JoinableTaskFactory.RunAsync(async () => { + using var _ = _ruleSetManager._listener.BeginAsyncOperation("IncludeUpdated"); await _ruleSetManager._threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(_disposalToken); IncludeUpdateCore(); - }, _disposalToken).CompletesAsyncOperation(_ruleSetManager._listener.BeginAsyncOperation("IncludeUpdated")); + }); } private void IncludeUpdateCore() From 1c8be66bf48f25ff18e44b530cf7fa5fdb46b2cc Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 21 Apr 2021 14:13:07 -0700 Subject: [PATCH 04/13] pr feedback --- .../RuleSets/VisualStudioRuleSetManager.RuleSetFile.cs | 2 +- .../Core/Impl/CodeModel/ProjectCodeModelFactory.cs | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/RuleSets/VisualStudioRuleSetManager.RuleSetFile.cs b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/RuleSets/VisualStudioRuleSetManager.RuleSetFile.cs index da52974359c9f..b77308868e46e 100644 --- a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/RuleSets/VisualStudioRuleSetManager.RuleSetFile.cs +++ b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/RuleSets/VisualStudioRuleSetManager.RuleSetFile.cs @@ -182,7 +182,7 @@ private void IncludeUpdated(object sender, string fileChanged) _ruleSetManager._threadingContext.JoinableTaskFactory.RunAsync(async () => { using var _ = _ruleSetManager._listener.BeginAsyncOperation("IncludeUpdated"); - await _ruleSetManager._threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(_disposalToken); + await _ruleSetManager._threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(alwaysYield: true, _disposalToken); IncludeUpdateCore(); }); } diff --git a/src/VisualStudio/Core/Impl/CodeModel/ProjectCodeModelFactory.cs b/src/VisualStudio/Core/Impl/CodeModel/ProjectCodeModelFactory.cs index 38b4cbf913c6a..92afdd703d706 100644 --- a/src/VisualStudio/Core/Impl/CodeModel/ProjectCodeModelFactory.cs +++ b/src/VisualStudio/Core/Impl/CodeModel/ProjectCodeModelFactory.cs @@ -71,14 +71,15 @@ private Task ProcessNextDocumentBatchAsync( // Now, enqueue foreground work to actually process these documents in a serialized and incremental // fashion. FireEventsForDocument will actually limit how much time it spends firing events so that it // doesn't saturate the UI thread. - Task.Run(async () => + _threadingContext.JoinableTaskFactory.RunAsync(async () => { - await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + using var _ = _listener.BeginAsyncOperation("CodeModelEvent"); + await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(alwaysYield: true, cancellationToken); FireEventsForDocument(documentId); - }, cancellationToken).CompletesAsyncOperation(_listener.BeginAsyncOperation("CodeModelEvent")); + }); } - return System.Threading.Tasks.Task.CompletedTask; + return Task.CompletedTask; bool FireEventsForDocument(DocumentId documentId) { From 7123c915b3fd154899ba084d0f9ae8ae36d1cc34 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 21 Apr 2021 14:32:10 -0700 Subject: [PATCH 05/13] Add delays back --- .../Def/Implementation/InfoBar/VisualStudioInfoBarService.cs | 1 + .../RuleSets/VisualStudioRuleSetManager.RuleSetFile.cs | 3 ++- .../Core/Impl/CodeModel/ProjectCodeModelFactory.cs | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/VisualStudio/Core/Def/Implementation/InfoBar/VisualStudioInfoBarService.cs b/src/VisualStudio/Core/Def/Implementation/InfoBar/VisualStudioInfoBarService.cs index 35cd555a99e19..8f55a3c0c023e 100644 --- a/src/VisualStudio/Core/Def/Implementation/InfoBar/VisualStudioInfoBarService.cs +++ b/src/VisualStudio/Core/Def/Implementation/InfoBar/VisualStudioInfoBarService.cs @@ -46,6 +46,7 @@ public void ShowInfoBar(string message, params InfoBarUI[] items) this.ThreadingContext.JoinableTaskFactory.RunAsync(async () => { using var _ = _listener.BeginAsyncOperation(nameof(ShowInfoBar)); + await System.Threading.Tasks.Task.Delay(15).ConfigureAwait(false); await this.ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(ThreadingContext.DisposalToken); if (TryGetInfoBarData(out var infoBarHost)) CreateInfoBar(infoBarHost, message, items); diff --git a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/RuleSets/VisualStudioRuleSetManager.RuleSetFile.cs b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/RuleSets/VisualStudioRuleSetManager.RuleSetFile.cs index b77308868e46e..71e1496f07de5 100644 --- a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/RuleSets/VisualStudioRuleSetManager.RuleSetFile.cs +++ b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/RuleSets/VisualStudioRuleSetManager.RuleSetFile.cs @@ -182,7 +182,8 @@ private void IncludeUpdated(object sender, string fileChanged) _ruleSetManager._threadingContext.JoinableTaskFactory.RunAsync(async () => { using var _ = _ruleSetManager._listener.BeginAsyncOperation("IncludeUpdated"); - await _ruleSetManager._threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(alwaysYield: true, _disposalToken); + await Task.Delay(15).ConfigureAwait(false); + await _ruleSetManager._threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(_disposalToken); IncludeUpdateCore(); }); } diff --git a/src/VisualStudio/Core/Impl/CodeModel/ProjectCodeModelFactory.cs b/src/VisualStudio/Core/Impl/CodeModel/ProjectCodeModelFactory.cs index 92afdd703d706..96aa2dc44537f 100644 --- a/src/VisualStudio/Core/Impl/CodeModel/ProjectCodeModelFactory.cs +++ b/src/VisualStudio/Core/Impl/CodeModel/ProjectCodeModelFactory.cs @@ -74,6 +74,7 @@ private Task ProcessNextDocumentBatchAsync( _threadingContext.JoinableTaskFactory.RunAsync(async () => { using var _ = _listener.BeginAsyncOperation("CodeModelEvent"); + await Task.Delay(15).ConfigureAwait(false); await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(alwaysYield: true, cancellationToken); FireEventsForDocument(documentId); }); From 07a4a4ea8793768858115bbda59037d298df69a8 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 21 Apr 2021 14:43:10 -0700 Subject: [PATCH 06/13] Restore delays --- ...ntingClassificationBufferTaggerProvider.Tagger.cs | 12 ++++++++---- ...eAndPrintingClassificationBufferTaggerProvider.cs | 3 --- .../InfoBar/VisualStudioInfoBarService.cs | 3 ++- .../VisualStudioRuleSetManager.RuleSetFile.cs | 3 ++- .../Core/Impl/CodeModel/ProjectCodeModelFactory.cs | 5 +++-- 5 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/EditorFeatures/Core/Implementation/Classification/CopyPasteAndPrintingClassificationBufferTaggerProvider.Tagger.cs b/src/EditorFeatures/Core/Implementation/Classification/CopyPasteAndPrintingClassificationBufferTaggerProvider.Tagger.cs index f4080617d83df..d2c83cc511455 100644 --- a/src/EditorFeatures/Core/Implementation/Classification/CopyPasteAndPrintingClassificationBufferTaggerProvider.Tagger.cs +++ b/src/EditorFeatures/Core/Implementation/Classification/CopyPasteAndPrintingClassificationBufferTaggerProvider.Tagger.cs @@ -104,10 +104,14 @@ private SnapshotSpan? CachedTaggedSpan private void OnEventSourceChanged(object sender, TaggerEventArgs _) { - _owner._notificationService.RegisterNotification( - OnEventSourceChanged_OnForeground, - _owner._asyncListener.BeginAsyncOperation("SemanticClassificationBufferTaggerProvider"), - _cancellationTokenSource.Token); + _owner.ThreadingContext.JoinableTaskFactory.RunAsync(async () => + { + using var _ = _owner._asyncListener.BeginAsyncOperation("SemanticClassificationBufferTaggerProvider"); + + await Task.Delay(15, _cancellationTokenSource.Token).ConfigureAwait(false); + await _owner.ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(_cancellationTokenSource.Token); + OnEventSourceChanged_OnForeground(); + }); } private void OnEventSourceChanged_OnForeground() diff --git a/src/EditorFeatures/Core/Implementation/Classification/CopyPasteAndPrintingClassificationBufferTaggerProvider.cs b/src/EditorFeatures/Core/Implementation/Classification/CopyPasteAndPrintingClassificationBufferTaggerProvider.cs index f2037a6b729b0..a6118f3c23c73 100644 --- a/src/EditorFeatures/Core/Implementation/Classification/CopyPasteAndPrintingClassificationBufferTaggerProvider.cs +++ b/src/EditorFeatures/Core/Implementation/Classification/CopyPasteAndPrintingClassificationBufferTaggerProvider.cs @@ -29,19 +29,16 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.Classification internal partial class CopyPasteAndPrintingClassificationBufferTaggerProvider : ForegroundThreadAffinitizedObject, ITaggerProvider { private readonly IAsynchronousOperationListener _asyncListener; - private readonly IForegroundNotificationService _notificationService; private readonly ClassificationTypeMap _typeMap; [ImportingConstructor] [SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")] public CopyPasteAndPrintingClassificationBufferTaggerProvider( IThreadingContext threadingContext, - IForegroundNotificationService notificationService, ClassificationTypeMap typeMap, IAsynchronousOperationListenerProvider listenerProvider) : base(threadingContext) { - _notificationService = notificationService; _typeMap = typeMap; _asyncListener = listenerProvider.GetListener(FeatureAttribute.Classification); } diff --git a/src/VisualStudio/Core/Def/Implementation/InfoBar/VisualStudioInfoBarService.cs b/src/VisualStudio/Core/Def/Implementation/InfoBar/VisualStudioInfoBarService.cs index 8f55a3c0c023e..23ac77a0396e1 100644 --- a/src/VisualStudio/Core/Def/Implementation/InfoBar/VisualStudioInfoBarService.cs +++ b/src/VisualStudio/Core/Def/Implementation/InfoBar/VisualStudioInfoBarService.cs @@ -46,7 +46,8 @@ public void ShowInfoBar(string message, params InfoBarUI[] items) this.ThreadingContext.JoinableTaskFactory.RunAsync(async () => { using var _ = _listener.BeginAsyncOperation(nameof(ShowInfoBar)); - await System.Threading.Tasks.Task.Delay(15).ConfigureAwait(false); + + await System.Threading.Tasks.Task.Delay(15, ThreadingContext.DisposalToken).ConfigureAwait(false); await this.ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(ThreadingContext.DisposalToken); if (TryGetInfoBarData(out var infoBarHost)) CreateInfoBar(infoBarHost, message, items); diff --git a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/RuleSets/VisualStudioRuleSetManager.RuleSetFile.cs b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/RuleSets/VisualStudioRuleSetManager.RuleSetFile.cs index 71e1496f07de5..a26a0c227e429 100644 --- a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/RuleSets/VisualStudioRuleSetManager.RuleSetFile.cs +++ b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/RuleSets/VisualStudioRuleSetManager.RuleSetFile.cs @@ -182,7 +182,8 @@ private void IncludeUpdated(object sender, string fileChanged) _ruleSetManager._threadingContext.JoinableTaskFactory.RunAsync(async () => { using var _ = _ruleSetManager._listener.BeginAsyncOperation("IncludeUpdated"); - await Task.Delay(15).ConfigureAwait(false); + + await Task.Delay(15, _disposalToken).ConfigureAwait(false); await _ruleSetManager._threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(_disposalToken); IncludeUpdateCore(); }); diff --git a/src/VisualStudio/Core/Impl/CodeModel/ProjectCodeModelFactory.cs b/src/VisualStudio/Core/Impl/CodeModel/ProjectCodeModelFactory.cs index 96aa2dc44537f..05842987884c8 100644 --- a/src/VisualStudio/Core/Impl/CodeModel/ProjectCodeModelFactory.cs +++ b/src/VisualStudio/Core/Impl/CodeModel/ProjectCodeModelFactory.cs @@ -74,8 +74,9 @@ private Task ProcessNextDocumentBatchAsync( _threadingContext.JoinableTaskFactory.RunAsync(async () => { using var _ = _listener.BeginAsyncOperation("CodeModelEvent"); - await Task.Delay(15).ConfigureAwait(false); - await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(alwaysYield: true, cancellationToken); + + await Task.Delay(15, cancellationToken).ConfigureAwait(false); + await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); FireEventsForDocument(documentId); }); } From de3ef6a27b5c850b503340b0451e360502f77720 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 21 Apr 2021 14:47:17 -0700 Subject: [PATCH 07/13] Fixup test --- .../Classification/CopyPasteAndPrintingClassifierTests.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/EditorFeatures/CSharpTest/Classification/CopyPasteAndPrintingClassifierTests.cs b/src/EditorFeatures/CSharpTest/Classification/CopyPasteAndPrintingClassifierTests.cs index 6e65f4658ae12..62457ed87a60a 100644 --- a/src/EditorFeatures/CSharpTest/Classification/CopyPasteAndPrintingClassifierTests.cs +++ b/src/EditorFeatures/CSharpTest/Classification/CopyPasteAndPrintingClassifierTests.cs @@ -36,7 +36,6 @@ public async Task TestGetTagsOnBufferTagger() var provider = new CopyPasteAndPrintingClassificationBufferTaggerProvider( workspace.ExportProvider.GetExportedValue(), - workspace.ExportProvider.GetExportedValue(), workspace.ExportProvider.GetExportedValue(), listenerProvider); From 5e49b71b837163072ba4ad83985049610ce2c8f8 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 21 Apr 2021 15:13:45 -0700 Subject: [PATCH 08/13] Invert loop --- ...assificationBufferTaggerProvider.Tagger.cs | 2 -- .../InfoBar/VisualStudioInfoBarService.cs | 2 -- .../VisualStudioRuleSetManager.RuleSetFile.cs | 2 -- .../Impl/CodeModel/ProjectCodeModelFactory.cs | 34 ++++++++++++------- 4 files changed, 22 insertions(+), 18 deletions(-) diff --git a/src/EditorFeatures/Core/Implementation/Classification/CopyPasteAndPrintingClassificationBufferTaggerProvider.Tagger.cs b/src/EditorFeatures/Core/Implementation/Classification/CopyPasteAndPrintingClassificationBufferTaggerProvider.Tagger.cs index d2c83cc511455..caf58c5875aee 100644 --- a/src/EditorFeatures/Core/Implementation/Classification/CopyPasteAndPrintingClassificationBufferTaggerProvider.Tagger.cs +++ b/src/EditorFeatures/Core/Implementation/Classification/CopyPasteAndPrintingClassificationBufferTaggerProvider.Tagger.cs @@ -107,8 +107,6 @@ private void OnEventSourceChanged(object sender, TaggerEventArgs _) _owner.ThreadingContext.JoinableTaskFactory.RunAsync(async () => { using var _ = _owner._asyncListener.BeginAsyncOperation("SemanticClassificationBufferTaggerProvider"); - - await Task.Delay(15, _cancellationTokenSource.Token).ConfigureAwait(false); await _owner.ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(_cancellationTokenSource.Token); OnEventSourceChanged_OnForeground(); }); diff --git a/src/VisualStudio/Core/Def/Implementation/InfoBar/VisualStudioInfoBarService.cs b/src/VisualStudio/Core/Def/Implementation/InfoBar/VisualStudioInfoBarService.cs index 23ac77a0396e1..35cd555a99e19 100644 --- a/src/VisualStudio/Core/Def/Implementation/InfoBar/VisualStudioInfoBarService.cs +++ b/src/VisualStudio/Core/Def/Implementation/InfoBar/VisualStudioInfoBarService.cs @@ -46,8 +46,6 @@ public void ShowInfoBar(string message, params InfoBarUI[] items) this.ThreadingContext.JoinableTaskFactory.RunAsync(async () => { using var _ = _listener.BeginAsyncOperation(nameof(ShowInfoBar)); - - await System.Threading.Tasks.Task.Delay(15, ThreadingContext.DisposalToken).ConfigureAwait(false); await this.ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(ThreadingContext.DisposalToken); if (TryGetInfoBarData(out var infoBarHost)) CreateInfoBar(infoBarHost, message, items); diff --git a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/RuleSets/VisualStudioRuleSetManager.RuleSetFile.cs b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/RuleSets/VisualStudioRuleSetManager.RuleSetFile.cs index a26a0c227e429..da52974359c9f 100644 --- a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/RuleSets/VisualStudioRuleSetManager.RuleSetFile.cs +++ b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/RuleSets/VisualStudioRuleSetManager.RuleSetFile.cs @@ -182,8 +182,6 @@ private void IncludeUpdated(object sender, string fileChanged) _ruleSetManager._threadingContext.JoinableTaskFactory.RunAsync(async () => { using var _ = _ruleSetManager._listener.BeginAsyncOperation("IncludeUpdated"); - - await Task.Delay(15, _disposalToken).ConfigureAwait(false); await _ruleSetManager._threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(_disposalToken); IncludeUpdateCore(); }); diff --git a/src/VisualStudio/Core/Impl/CodeModel/ProjectCodeModelFactory.cs b/src/VisualStudio/Core/Impl/CodeModel/ProjectCodeModelFactory.cs index 05842987884c8..29caaccd045bd 100644 --- a/src/VisualStudio/Core/Impl/CodeModel/ProjectCodeModelFactory.cs +++ b/src/VisualStudio/Core/Impl/CodeModel/ProjectCodeModelFactory.cs @@ -66,20 +66,30 @@ public ProjectCodeModelFactory( private Task ProcessNextDocumentBatchAsync( ImmutableArray documentIds, CancellationToken cancellationToken) { - foreach (var documentId in documentIds) + // Now, enqueue foreground work to actually process these documents in a serialized and incremental + // fashion. FireEventsForDocument will actually limit how much time it spends firing events so that it + // doesn't saturate the UI thread. + _threadingContext.JoinableTaskFactory.RunAsync(async () => { - // Now, enqueue foreground work to actually process these documents in a serialized and incremental - // fashion. FireEventsForDocument will actually limit how much time it spends firing events so that it - // doesn't saturate the UI thread. - _threadingContext.JoinableTaskFactory.RunAsync(async () => - { - using var _ = _listener.BeginAsyncOperation("CodeModelEvent"); + using var _ = _listener.BeginAsyncOperation("CodeModelEvent"); - await Task.Delay(15, cancellationToken).ConfigureAwait(false); - await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); - FireEventsForDocument(documentId); - }); - } + await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + + foreach (var documentId in documentIds) + { + var stopwatch = SharedStopwatch.StartNew(); + + // As long as we have events to fire for this document, keep yielding so we don't + // bog down the UI thread. + while (FireEventsForDocument(documentId)) + await Task.Delay(15).ConfigureAwait(true); + + // if we've also taken a lot of time processing documents on the UI thread, yield + // so we don't bog things down. + if (stopwatch.Elapsed.TotalMilliseconds > 50) + await Task.Delay(15).ConfigureAwait(true); + } + }); return Task.CompletedTask; From 99a7853c728b69cfa3bccf78417cd6b23191007a Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 21 Apr 2021 15:15:28 -0700 Subject: [PATCH 09/13] constant --- .../Core/Impl/CodeModel/ProjectCodeModelFactory.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/VisualStudio/Core/Impl/CodeModel/ProjectCodeModelFactory.cs b/src/VisualStudio/Core/Impl/CodeModel/ProjectCodeModelFactory.cs index 29caaccd045bd..80da65c8e0d84 100644 --- a/src/VisualStudio/Core/Impl/CodeModel/ProjectCodeModelFactory.cs +++ b/src/VisualStudio/Core/Impl/CodeModel/ProjectCodeModelFactory.cs @@ -66,6 +66,8 @@ public ProjectCodeModelFactory( private Task ProcessNextDocumentBatchAsync( ImmutableArray documentIds, CancellationToken cancellationToken) { + const int MinimumDelayBetweenProcessing = 50; + // Now, enqueue foreground work to actually process these documents in a serialized and incremental // fashion. FireEventsForDocument will actually limit how much time it spends firing events so that it // doesn't saturate the UI thread. @@ -82,12 +84,12 @@ private Task ProcessNextDocumentBatchAsync( // As long as we have events to fire for this document, keep yielding so we don't // bog down the UI thread. while (FireEventsForDocument(documentId)) - await Task.Delay(15).ConfigureAwait(true); + await Task.Delay(MinimumDelayBetweenProcessing).ConfigureAwait(true); // if we've also taken a lot of time processing documents on the UI thread, yield // so we don't bog things down. - if (stopwatch.Elapsed.TotalMilliseconds > 50) - await Task.Delay(15).ConfigureAwait(true); + if (stopwatch.Elapsed.TotalMilliseconds > MinimumDelayBetweenProcessing) + await Task.Delay(MinimumDelayBetweenProcessing).ConfigureAwait(true); } }); From f4f5eed6d62f311d17a2872a33509494c5fdc38f Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 21 Apr 2021 16:35:35 -0700 Subject: [PATCH 10/13] Do not fire TagsChanged for the copy/paste classifier. --- ...ClassificationBufferTaggerProvider.Tagger.cs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/EditorFeatures/Core/Implementation/Classification/CopyPasteAndPrintingClassificationBufferTaggerProvider.Tagger.cs b/src/EditorFeatures/Core/Implementation/Classification/CopyPasteAndPrintingClassificationBufferTaggerProvider.Tagger.cs index f4080617d83df..06215a568c9aa 100644 --- a/src/EditorFeatures/Core/Implementation/Classification/CopyPasteAndPrintingClassificationBufferTaggerProvider.Tagger.cs +++ b/src/EditorFeatures/Core/Implementation/Classification/CopyPasteAndPrintingClassificationBufferTaggerProvider.Tagger.cs @@ -58,6 +58,10 @@ public Tagger( ConnectToEventSource(); } + // Explicitly a no-op. This classifier does not support change notifications. See comment in + // OnEventSourceChanged_OnForeground for more details. + public event EventHandler TagsChanged { add { } remove { } } + public void Dispose() { this.AssertIsForeground(); @@ -118,12 +122,17 @@ private void OnEventSourceChanged_OnForeground() this.CachedTags = null; this.CachedTaggedSpan = null; - // And notify any concerned parties that we have new tags. - this.TagsChanged?.Invoke(this, new SnapshotSpanEventArgs(_subjectBuffer.CurrentSnapshot.GetFullSpan())); + // Note: we explicitly do *not* call into TagsChanged here. This type exists only for the copy/paste + // scenario, and in the case the editor always calls into us for the span in question, ignoring + // TagsChanged, as per DPugh: + // + // For rich text copy, we always call the buffer classifier to get the classifications of the copied + // text. It ignores any tags changed events. + // + // It's important that we do not call TagsChanged here as the only thing we could do is notify that the + // entire doc is changed, and that incurs a heavy cost for the editor reacting to that notification. } - public event EventHandler TagsChanged; - public IEnumerable> GetTags(NormalizedSnapshotSpanCollection spans) { this.AssertIsForeground(); From 6d75a5c9528404d149034aad6c4f3ea558f28fa2 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 21 Apr 2021 17:23:12 -0700 Subject: [PATCH 11/13] REvert codemodel part --- .../CodeModel/FileCodeModelTestHelpers.cs | 3 ++ .../Impl/CodeModel/ProjectCodeModelFactory.cs | 41 +++++++------------ .../CodeModel/CodeModelTestHelpers.vb | 4 ++ 3 files changed, 21 insertions(+), 27 deletions(-) diff --git a/src/VisualStudio/CSharp/Test/CodeModel/FileCodeModelTestHelpers.cs b/src/VisualStudio/CSharp/Test/CodeModel/FileCodeModelTestHelpers.cs index e3d9b1fc23244..9882123bc1152 100644 --- a/src/VisualStudio/CSharp/Test/CodeModel/FileCodeModelTestHelpers.cs +++ b/src/VisualStudio/CSharp/Test/CodeModel/FileCodeModelTestHelpers.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Runtime.ExceptionServices; +using Microsoft.CodeAnalysis.Editor; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; using Microsoft.CodeAnalysis.Shared.TestHooks; @@ -40,6 +41,7 @@ public static (TestWorkspace workspace, VisualStudioWorkspace extraWorkspaceToDi var visualStudioWorkspaceMock = new MockVisualStudioWorkspace(workspace); var threadingContext = workspace.ExportProvider.GetExportedValue(); + var notificationService = workspace.ExportProvider.GetExportedValue(); var listenerProvider = workspace.ExportProvider.GetExportedValue(); var state = new CodeModelState( @@ -51,6 +53,7 @@ public static (TestWorkspace workspace, VisualStudioWorkspace extraWorkspaceToDi visualStudioWorkspaceMock, serviceProvider, threadingContext, + notificationService, listenerProvider)); var codeModel = FileCodeModel.Create(state, null, document, new MockTextManagerAdapter()).Handle; diff --git a/src/VisualStudio/Core/Impl/CodeModel/ProjectCodeModelFactory.cs b/src/VisualStudio/Core/Impl/CodeModel/ProjectCodeModelFactory.cs index 80da65c8e0d84..f6f79adc207ee 100644 --- a/src/VisualStudio/Core/Impl/CodeModel/ProjectCodeModelFactory.cs +++ b/src/VisualStudio/Core/Impl/CodeModel/ProjectCodeModelFactory.cs @@ -31,6 +31,7 @@ internal sealed class ProjectCodeModelFactory : IProjectCodeModelFactory private readonly IThreadingContext _threadingContext; + private readonly IForegroundNotificationService _notificationService; private readonly IAsynchronousOperationListener _listener; private readonly AsyncBatchingWorkQueue _documentsToFireEventsFor; @@ -40,12 +41,14 @@ public ProjectCodeModelFactory( VisualStudioWorkspace visualStudioWorkspace, [Import(typeof(SVsServiceProvider))] IServiceProvider serviceProvider, IThreadingContext threadingContext, + IForegroundNotificationService notificationService, IAsynchronousOperationListenerProvider listenerProvider) { _visualStudioWorkspace = visualStudioWorkspace; _serviceProvider = serviceProvider; _threadingContext = threadingContext; + _notificationService = notificationService; _listener = listenerProvider.GetListener(FeatureAttribute.CodeModel); // Queue up notifications we hear about docs changing. that way we don't have to fire events multiple times @@ -63,37 +66,21 @@ public ProjectCodeModelFactory( _visualStudioWorkspace.WorkspaceChanged += OnWorkspaceChanged; } - private Task ProcessNextDocumentBatchAsync( + private System.Threading.Tasks.Task ProcessNextDocumentBatchAsync( ImmutableArray documentIds, CancellationToken cancellationToken) { - const int MinimumDelayBetweenProcessing = 50; - - // Now, enqueue foreground work to actually process these documents in a serialized and incremental - // fashion. FireEventsForDocument will actually limit how much time it spends firing events so that it - // doesn't saturate the UI thread. - _threadingContext.JoinableTaskFactory.RunAsync(async () => + foreach (var documentId in documentIds) { - using var _ = _listener.BeginAsyncOperation("CodeModelEvent"); - - await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); - - foreach (var documentId in documentIds) - { - var stopwatch = SharedStopwatch.StartNew(); - - // As long as we have events to fire for this document, keep yielding so we don't - // bog down the UI thread. - while (FireEventsForDocument(documentId)) - await Task.Delay(MinimumDelayBetweenProcessing).ConfigureAwait(true); - - // if we've also taken a lot of time processing documents on the UI thread, yield - // so we don't bog things down. - if (stopwatch.Elapsed.TotalMilliseconds > MinimumDelayBetweenProcessing) - await Task.Delay(MinimumDelayBetweenProcessing).ConfigureAwait(true); - } - }); + // Now, enqueue foreground work to actually process these documents in a serialized and incremental + // fashion. FireEventsForDocument will actually limit how much time it spends firing events so that it + // doesn't saturate the UI thread. + _notificationService.RegisterNotification( + () => FireEventsForDocument(documentId), + _listener.BeginAsyncOperation("CodeModelEvent"), + cancellationToken); + } - return Task.CompletedTask; + return System.Threading.Tasks.Task.CompletedTask; bool FireEventsForDocument(DocumentId documentId) { diff --git a/src/VisualStudio/TestUtilities2/CodeModel/CodeModelTestHelpers.vb b/src/VisualStudio/TestUtilities2/CodeModel/CodeModelTestHelpers.vb index 076bf061de3d8..96c66d32b7186 100644 --- a/src/VisualStudio/TestUtilities2/CodeModel/CodeModelTestHelpers.vb +++ b/src/VisualStudio/TestUtilities2/CodeModel/CodeModelTestHelpers.vb @@ -6,7 +6,9 @@ Imports System.Runtime.CompilerServices Imports System.Runtime.ExceptionServices Imports System.Runtime.InteropServices Imports EnvDTE +Imports EnvDTE80 Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Editor Imports Microsoft.CodeAnalysis.Editor.Shared.Utilities Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces Imports Microsoft.CodeAnalysis.Shared.TestHooks @@ -56,6 +58,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.CodeModel Dim project = workspace.CurrentSolution.Projects.Single() Dim threadingContext = workspace.ExportProvider.GetExportedValue(Of IThreadingContext) + Dim notificationService = workspace.ExportProvider.GetExportedValue(Of IForegroundNotificationService) Dim listenerProvider = workspace.ExportProvider.GetExportedValue(Of AsynchronousOperationListenerProvider)() Dim state = New CodeModelState( @@ -67,6 +70,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.CodeModel mockVisualStudioWorkspace, mockServiceProvider, threadingContext, + notificationService, listenerProvider)) Dim projectCodeModel = DirectCast(state.ProjectCodeModelFactory.CreateProjectCodeModel(project.Id, Nothing), ProjectCodeModel) From 1f310d77c22f97bc6c811be133b84ea2ff858a9a Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 21 Apr 2021 17:24:50 -0700 Subject: [PATCH 12/13] Yield --- .../Def/Implementation/InfoBar/VisualStudioInfoBarService.cs | 2 +- .../RuleSets/VisualStudioRuleSetManager.RuleSetFile.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/VisualStudio/Core/Def/Implementation/InfoBar/VisualStudioInfoBarService.cs b/src/VisualStudio/Core/Def/Implementation/InfoBar/VisualStudioInfoBarService.cs index 35cd555a99e19..1625c3cc568ed 100644 --- a/src/VisualStudio/Core/Def/Implementation/InfoBar/VisualStudioInfoBarService.cs +++ b/src/VisualStudio/Core/Def/Implementation/InfoBar/VisualStudioInfoBarService.cs @@ -46,7 +46,7 @@ public void ShowInfoBar(string message, params InfoBarUI[] items) this.ThreadingContext.JoinableTaskFactory.RunAsync(async () => { using var _ = _listener.BeginAsyncOperation(nameof(ShowInfoBar)); - await this.ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(ThreadingContext.DisposalToken); + await this.ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(alwaysYield: true, ThreadingContext.DisposalToken); if (TryGetInfoBarData(out var infoBarHost)) CreateInfoBar(infoBarHost, message, items); }); diff --git a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/RuleSets/VisualStudioRuleSetManager.RuleSetFile.cs b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/RuleSets/VisualStudioRuleSetManager.RuleSetFile.cs index da52974359c9f..b77308868e46e 100644 --- a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/RuleSets/VisualStudioRuleSetManager.RuleSetFile.cs +++ b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/RuleSets/VisualStudioRuleSetManager.RuleSetFile.cs @@ -182,7 +182,7 @@ private void IncludeUpdated(object sender, string fileChanged) _ruleSetManager._threadingContext.JoinableTaskFactory.RunAsync(async () => { using var _ = _ruleSetManager._listener.BeginAsyncOperation("IncludeUpdated"); - await _ruleSetManager._threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(_disposalToken); + await _ruleSetManager._threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(alwaysYield: true, _disposalToken); IncludeUpdateCore(); }); } From eff66efd4f5deadd7f28aeca46b4eca8e6cf7fa6 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 21 Apr 2021 17:28:43 -0700 Subject: [PATCH 13/13] Remove yield --- .../Def/Implementation/InfoBar/VisualStudioInfoBarService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/VisualStudio/Core/Def/Implementation/InfoBar/VisualStudioInfoBarService.cs b/src/VisualStudio/Core/Def/Implementation/InfoBar/VisualStudioInfoBarService.cs index 1625c3cc568ed..35cd555a99e19 100644 --- a/src/VisualStudio/Core/Def/Implementation/InfoBar/VisualStudioInfoBarService.cs +++ b/src/VisualStudio/Core/Def/Implementation/InfoBar/VisualStudioInfoBarService.cs @@ -46,7 +46,7 @@ public void ShowInfoBar(string message, params InfoBarUI[] items) this.ThreadingContext.JoinableTaskFactory.RunAsync(async () => { using var _ = _listener.BeginAsyncOperation(nameof(ShowInfoBar)); - await this.ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(alwaysYield: true, ThreadingContext.DisposalToken); + await this.ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(ThreadingContext.DisposalToken); if (TryGetInfoBarData(out var infoBarHost)) CreateInfoBar(infoBarHost, message, items); });