From 9ce13dd2ddb69d7ace2dc615f5ed0eb750f1e9be Mon Sep 17 00:00:00 2001 From: Eric Arndt Date: Tue, 13 Aug 2024 15:26:33 -0700 Subject: [PATCH 1/2] Avoid repeated switches to main thread to check if in command line mode --- .../LanguageServices/LanguageServiceHost.cs | 2 - .../Shell/VSShellServices.cs | 74 ++++++++----------- 2 files changed, 32 insertions(+), 44 deletions(-) diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/LanguageServiceHost.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/LanguageServiceHost.cs index a1529143bae..d62c49e424a 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/LanguageServiceHost.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/LanguageServiceHost.cs @@ -80,8 +80,6 @@ public LanguageServiceHost( _isEnabled = new( async () => { - await threadingService.JoinableTaskFactory.SwitchToMainThreadAsync(); - // If VS is running in command line mode (e.g. "devenv.exe /build my.sln"), // language services should not be enabled. The one exception to this is // when we're populating a solution cache via "/populateSolutionCache". diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Shell/VSShellServices.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Shell/VSShellServices.cs index 886554e12ab..9cd8f11a1d4 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Shell/VSShellServices.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Shell/VSShellServices.cs @@ -2,6 +2,7 @@ using Microsoft.VisualStudio.ProjectSystem; using Microsoft.VisualStudio.ProjectSystem.VS; +using Microsoft.VisualStudio.ProjectSystem.VS.Interop; using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Threading; @@ -11,65 +12,54 @@ namespace Microsoft.VisualStudio.Shell; [AppliesTo(ProjectCapabilities.AlwaysApplicable)] internal class VSShellServices : IVsShellServices { - - private readonly AsyncLazy<(bool IsCommandLineMode, bool IsPopulateSolutionCacheMode)> _initialization; + private static readonly object s_lockObject = new(); + private static Task? s_isCommandLine; + private static Task? s_isPopulateCache; [ImportingConstructor] public VSShellServices( - IVsUIService vsShellService, - IVsUIService commandLineService, - JoinableTaskContext joinableTaskContext) + IVsService vsAppId, + IVsService commandLine) { - _initialization = new( - async () => - { - // Initialisation must occur on the main thread. - await joinableTaskContext.Factory.SwitchToMainThreadAsync(); - - IVsShell? vsShell = vsShellService.Value; - IVsAppCommandLine? commandLine = commandLineService.Value; - - bool isCommandLineMode = IsCommandLineMode(); + lock (s_lockObject) + { + s_isCommandLine ??= IsCommandLineModeAsync(vsAppId); + s_isPopulateCache ??= IsPopulateSolutionCacheModeAsync(commandLine); - if (isCommandLineMode) - { - return (IsCommandLineMode: true, IsPopulateSolutionCacheMode()); - } + } - return (false, false); - - bool IsCommandLineMode() - { - Assumes.Present(vsShell); - - int hr = vsShell.GetProperty((int)__VSSPROPID.VSSPROPID_IsInCommandLineMode, out object result); + static async Task IsCommandLineModeAsync(IVsService vsAppIdService) + { + IVsAppId? vsAppId = await vsAppIdService.GetValueOrNullAsync(); + if (vsAppId is not null) + { + const int VSAPROPID_IsInCommandLineMode = (int)Microsoft.Internal.VisualStudio.AppId.Interop.__VSAPROPID10.VSAPROPID_IsInCommandLineMode; + return ErrorHandler.Succeeded(vsAppId.GetProperty(VSAPROPID_IsInCommandLineMode, out object o)) && o is bool isInCommandLineMode && isInCommandLineMode; + } - return ErrorHandler.Succeeded(hr) - && result is bool isCommandLineMode - && isCommandLineMode; - } + return false; + } - bool IsPopulateSolutionCacheMode() - { - if (commandLine is null) - return false; + static async Task IsPopulateSolutionCacheModeAsync(IVsService commandLineService) + { + IVsAppCommandLine? commandLine = await commandLineService.GetValueOrNullAsync(); + if (commandLine is null) + return false; - int hr = commandLine.GetOption("populateSolutionCache", out int populateSolutionCache, out string commandValue); + int hr = commandLine.GetOption("populateSolutionCache", out int populateSolutionCache, out string _); - return ErrorHandler.Succeeded(hr) - && Convert.ToBoolean(populateSolutionCache); - } - }, - joinableTaskContext.Factory); + return ErrorHandler.Succeeded(hr) + && Convert.ToBoolean(populateSolutionCache); + } } public async Task IsCommandLineModeAsync(CancellationToken cancellationToken) { - return (await _initialization.GetValueAsync(cancellationToken)).IsCommandLineMode; + return s_isCommandLine is not null && await s_isCommandLine; } public async Task IsPopulateSolutionCacheModeAsync(CancellationToken cancellationToken) { - return (await _initialization.GetValueAsync(cancellationToken)).IsPopulateSolutionCacheMode; + return s_isPopulateCache is not null && await s_isPopulateCache; } } From d43d49dd63ce62ec35368673d39ec3d98c0a341a Mon Sep 17 00:00:00 2001 From: Eric Arndt Date: Thu, 5 Sep 2024 09:07:17 -0700 Subject: [PATCH 2/2] PR feedback --- .../Shell/VSShellServices.cs | 21 +++++++------------ 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Shell/VSShellServices.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Shell/VSShellServices.cs index 9cd8f11a1d4..9b650cc5c6f 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Shell/VSShellServices.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Shell/VSShellServices.cs @@ -1,10 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. The .NET Foundation licenses this file to you under the MIT license. See the LICENSE.md file in the project root for more information. using Microsoft.VisualStudio.ProjectSystem; -using Microsoft.VisualStudio.ProjectSystem.VS; using Microsoft.VisualStudio.ProjectSystem.VS.Interop; using Microsoft.VisualStudio.Shell.Interop; -using Microsoft.VisualStudio.Threading; namespace Microsoft.VisualStudio.Shell; @@ -12,21 +10,16 @@ namespace Microsoft.VisualStudio.Shell; [AppliesTo(ProjectCapabilities.AlwaysApplicable)] internal class VSShellServices : IVsShellServices { - private static readonly object s_lockObject = new(); - private static Task? s_isCommandLine; - private static Task? s_isPopulateCache; + private readonly Task _isCommandLine; + private readonly Task _isPopulateCache; [ImportingConstructor] public VSShellServices( IVsService vsAppId, IVsService commandLine) { - lock (s_lockObject) - { - s_isCommandLine ??= IsCommandLineModeAsync(vsAppId); - s_isPopulateCache ??= IsPopulateSolutionCacheModeAsync(commandLine); - - } + _isCommandLine = IsCommandLineModeAsync(vsAppId); + _isPopulateCache = IsPopulateSolutionCacheModeAsync(commandLine); static async Task IsCommandLineModeAsync(IVsService vsAppIdService) { @@ -34,7 +27,7 @@ static async Task IsCommandLineModeAsync(IVsService vsAppIdServi if (vsAppId is not null) { const int VSAPROPID_IsInCommandLineMode = (int)Microsoft.Internal.VisualStudio.AppId.Interop.__VSAPROPID10.VSAPROPID_IsInCommandLineMode; - return ErrorHandler.Succeeded(vsAppId.GetProperty(VSAPROPID_IsInCommandLineMode, out object o)) && o is bool isInCommandLineMode && isInCommandLineMode; + return ErrorHandler.Succeeded(vsAppId.GetProperty(VSAPROPID_IsInCommandLineMode, out object o)) && o is true; } return false; @@ -55,11 +48,11 @@ static async Task IsPopulateSolutionCacheModeAsync(IVsService IsCommandLineModeAsync(CancellationToken cancellationToken) { - return s_isCommandLine is not null && await s_isCommandLine; + return await _isCommandLine; } public async Task IsPopulateSolutionCacheModeAsync(CancellationToken cancellationToken) { - return s_isPopulateCache is not null && await s_isPopulateCache; + return await _isPopulateCache; } }