From 9268db6349b3f31c2d5b16e6c474a572e4882966 Mon Sep 17 00:00:00 2001 From: Drew Noakes Date: Tue, 28 Nov 2023 14:05:57 +1100 Subject: [PATCH] Defer joining ProjectBuildRuleSource until solution loaded During solution load we run evaluations but not DTB. CPS has a feature that detects when the main thread needs DTB results and allows the DTB to run before solution load completes, in order to avoid deadlocks. While it's fine to create a dataflow subscription for build data during solution load, the joining of upstream data sources can cause CPS to accidentally think that DTB is blocking the UI thread. There's only one gate for the whole solution, so even a single project in this state will cause all projects to build. In order to avoid this, we defer joining upstream sources until after the solution has loaded. --- .../VS/LanguageServices/WorkspaceFactory.cs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/WorkspaceFactory.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/WorkspaceFactory.cs index 4ecbabd1b5a..8b28d34720e 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/WorkspaceFactory.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/WorkspaceFactory.cs @@ -20,6 +20,7 @@ internal class WorkspaceFactory : IWorkspaceFactory private readonly UnconfiguredProject _unconfiguredProject; private readonly IProjectService _projectService; private readonly IProjectThreadingService _threadingService; + private readonly IUnconfiguredProjectTasksService _tasksService; private readonly IManagedProjectDiagnosticOutputService _logger; private readonly IDataProgressTrackerService _dataProgressTrackerService; private readonly IActiveEditorContextTracker _activeWorkspaceProjectContextTracker; @@ -46,6 +47,7 @@ public WorkspaceFactory( _unconfiguredProject = unconfiguredProject; _projectService = projectService; _threadingService = threadingService; + _tasksService = tasksService; _logger = logger; _dataProgressTrackerService = dataProgressTrackerService; _activeWorkspaceProjectContextTracker = activeWorkspaceProjectContextTracker; @@ -146,10 +148,20 @@ var buildTransformBlock cancellationToken: cancellationToken), buildTransformBlock.LinkTo(orderingBlock, DataflowOption.PropagateCompletion), - - ProjectDataSources.JoinUpstreamDataSources(joinableTaskFactory, _projectService.Services.FaultHandler, source.ActiveConfiguredProjectSource, source.ProjectBuildRuleSource) }); + // During solution load we run evaluations but not DTB. CPS has a feature that detects when the main thread + // needs DTB results and allows the DTB to run before solution load completes, in order to avoid deadlocks. + // While it's fine to create a dataflow subscription for build data during solution load, the joining of + // upstream data sources can cause CPS to accidentally think that DTB is blocking the UI thread. There's only + // one gate for the whole solution, so even a single project in this state will cause all projects to build. + // In order to avoid this, we defer joining upstream sources until after the solution has loaded. + _ = _tasksService.SolutionLoadedInHost.ContinueWith( + _ => workspace.ChainDisposal(ProjectDataSources.JoinUpstreamDataSources(joinableTaskFactory, _projectService.Services.FaultHandler, source.ActiveConfiguredProjectSource, source.ProjectBuildRuleSource)), + CancellationToken.None, + TaskContinuationOptions.None, + TaskScheduler.Default); + #endregion return workspace;