From 89d94a2d7089dbdbe2ac58be923788d2e7c204d6 Mon Sep 17 00:00:00 2001 From: Heejae Chang Date: Tue, 14 Mar 2017 04:40:28 -0700 Subject: [PATCH 1/2] check remote host being available. when host is shutting down it is possible GetRemoteHostClient return null even though host itself support remote hosting. --- .../SymbolSearch/SymbolSearchUpdateEngineFactory.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/Workspaces/Core/Desktop/SymbolSearch/SymbolSearchUpdateEngineFactory.cs b/src/Workspaces/Core/Desktop/SymbolSearch/SymbolSearchUpdateEngineFactory.cs index ccf5e3382f5d..e0b6c13106fb 100644 --- a/src/Workspaces/Core/Desktop/SymbolSearch/SymbolSearchUpdateEngineFactory.cs +++ b/src/Workspaces/Core/Desktop/SymbolSearch/SymbolSearchUpdateEngineFactory.cs @@ -76,7 +76,11 @@ private async void OnConnectionChanged(object sender, bool connected) _sessionDoNotAccessDirectly = null; _client = await _workspace.GetRemoteHostClientAsync(CancellationToken.None).ConfigureAwait(false); - _client.ConnectionChanged += OnConnectionChanged; + if (_client != null) + { + // client can be null if host is shutting down + _client.ConnectionChanged += OnConnectionChanged; + } } } @@ -89,6 +93,12 @@ private async void OnConnectionChanged(object sender, bool connected) return _sessionDoNotAccessDirectly; } + if (_client == null) + { + // client can be null if host is shutting down + return null; + } + // We create a single session and use it for the entire lifetime of this process. // That single session will be used to do all communication with the remote process. // This is because each session will cause a new instance of the RemoteSymbolSearchUpdateEngine From d6939b02a936357fc6443406d1fca199e663d90f Mon Sep 17 00:00:00 2001 From: Heejae Chang Date: Tue, 14 Mar 2017 12:11:29 -0700 Subject: [PATCH 2/2] renamed GetRemoteHostClientAsync to TryRemoteHostClientAsync to make it clear that it can return null when remote host is not available. left GetRemotehostClientAsync there as Obsolete so that this doesnt break other team such as LUT. will remove it once other team moved to new bits. --- .../AbstractDesignerAttributeService.cs | 2 +- .../Diagnostics/EngineV2/DiagnosticAnalyzerExecutor.cs | 2 +- .../NavigateTo/AbstractNavigateToSearchService.Remote.cs | 2 +- .../Portable/TodoComments/AbstractTodoCommentService.cs | 2 +- ...oteHostClientServiceFactory.RemoteHostClientService.cs | 5 +++++ .../Core/Next/CodeLens/RemoteCodeLensReferencesService.cs | 8 ++++---- .../VisualStudioDiagnosticAnalyzerExecutorTests.cs | 2 +- .../Razor/RazorLanguageServiceClientFactory.cs | 2 +- .../SymbolSearch/SymbolSearchUpdateEngineFactory.cs | 4 ++-- .../Core/Portable/FindSymbols/SymbolFinder_Remote.cs | 4 ++-- ...oteHostClientServiceFactory.RemoteHostClientService.cs | 6 ++++++ .../Core/Portable/Remote/IRemoteHostClientService.cs | 3 +++ .../Core/Portable/Remote/RemoteHostClientExtensions.cs | 4 ++-- 13 files changed, 30 insertions(+), 16 deletions(-) diff --git a/src/Features/Core/Portable/DesignerAttributes/AbstractDesignerAttributeService.cs b/src/Features/Core/Portable/DesignerAttributes/AbstractDesignerAttributeService.cs index 39b97d194b48..a2672a62b64e 100644 --- a/src/Features/Core/Portable/DesignerAttributes/AbstractDesignerAttributeService.cs +++ b/src/Features/Core/Portable/DesignerAttributes/AbstractDesignerAttributeService.cs @@ -35,7 +35,7 @@ public async Task ScanDesignerAttributesAsync(Document // same service run in both inproc and remote host, but remote host will not have RemoteHostClient service, // so inproc one will always run - var client = await workspace.GetRemoteHostClientAsync(cancellationToken).ConfigureAwait(false); + var client = await workspace.TryGetRemoteHostClientAsync(cancellationToken).ConfigureAwait(false); if (client != null && !document.IsOpen()) { // run designer attributes scanner on remote host diff --git a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticAnalyzerExecutor.cs b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticAnalyzerExecutor.cs index 6b789a4d9b78..89627512a2b2 100644 --- a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticAnalyzerExecutor.cs +++ b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticAnalyzerExecutor.cs @@ -65,7 +65,7 @@ public async Task GetRemoteHostClientAsync(Project pro return null; } - return await project.Solution.Workspace.GetRemoteHostClientAsync(cancellationToken).ConfigureAwait(false); + return await project.Solution.Workspace.TryGetRemoteHostClientAsync(cancellationToken).ConfigureAwait(false); } } } \ No newline at end of file diff --git a/src/Features/Core/Portable/TodoComments/AbstractTodoCommentService.cs b/src/Features/Core/Portable/TodoComments/AbstractTodoCommentService.cs index 516e98cdfe6b..c3b2d08c2313 100644 --- a/src/Features/Core/Portable/TodoComments/AbstractTodoCommentService.cs +++ b/src/Features/Core/Portable/TodoComments/AbstractTodoCommentService.cs @@ -26,7 +26,7 @@ public async Task> GetTodoCommentsAsync(Document document, Im { // same service run in both inproc and remote host, but remote host will not have RemoteHostClient service, // so inproc one will always run - var client = await document.Project.Solution.Workspace.GetRemoteHostClientAsync(cancellationToken).ConfigureAwait(false); + var client = await document.Project.Solution.Workspace.TryGetRemoteHostClientAsync(cancellationToken).ConfigureAwait(false); if (client != null && !document.IsOpen()) { // run todo scanner on remote host. diff --git a/src/VisualStudio/Core/Def/Implementation/Remote/RemoteHostClientServiceFactory.RemoteHostClientService.cs b/src/VisualStudio/Core/Def/Implementation/Remote/RemoteHostClientServiceFactory.RemoteHostClientService.cs index 6ba0ffe12ceb..14afca7e2e10 100644 --- a/src/VisualStudio/Core/Def/Implementation/Remote/RemoteHostClientServiceFactory.RemoteHostClientService.cs +++ b/src/VisualStudio/Core/Def/Implementation/Remote/RemoteHostClientServiceFactory.RemoteHostClientService.cs @@ -130,6 +130,11 @@ public void Disable() } public Task GetRemoteHostClientAsync(CancellationToken cancellationToken) + { + return TryGetRemoteHostClientAsync(cancellationToken); + } + + public Task TryGetRemoteHostClientAsync(CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); diff --git a/src/VisualStudio/Core/Next/CodeLens/RemoteCodeLensReferencesService.cs b/src/VisualStudio/Core/Next/CodeLens/RemoteCodeLensReferencesService.cs index 019734cf1b7d..af13826508b1 100644 --- a/src/VisualStudio/Core/Next/CodeLens/RemoteCodeLensReferencesService.cs +++ b/src/VisualStudio/Core/Next/CodeLens/RemoteCodeLensReferencesService.cs @@ -25,7 +25,7 @@ public async Task GetReferenceCountAsync(Solution solution, Docu return null; } - var remoteHostClient = await solution.Workspace.Services.GetService().GetRemoteHostClientAsync(cancellationToken).ConfigureAwait(false); + var remoteHostClient = await solution.Workspace.Services.GetService().TryGetRemoteHostClientAsync(cancellationToken).ConfigureAwait(false); if (remoteHostClient == null) { // remote host is not running. this can happen if remote host is disabled. @@ -46,7 +46,7 @@ public async Task> FindReferenceLocatio return null; } - var remoteHostClient = await solution.Workspace.Services.GetService().GetRemoteHostClientAsync(cancellationToken).ConfigureAwait(false); + var remoteHostClient = await solution.Workspace.Services.GetService().TryGetRemoteHostClientAsync(cancellationToken).ConfigureAwait(false); if (remoteHostClient == null) { // remote host is not running. this can happen if remote host is disabled. @@ -67,7 +67,7 @@ public async Task> FindReferenceMethodsAs return null; } - var remoteHostClient = await solution.Workspace.Services.GetService().GetRemoteHostClientAsync(cancellationToken).ConfigureAwait(false); + var remoteHostClient = await solution.Workspace.Services.GetService().TryGetRemoteHostClientAsync(cancellationToken).ConfigureAwait(false); if (remoteHostClient == null) { // remote host is not running. this can happen if remote host is disabled. @@ -88,7 +88,7 @@ public async Task GetFullyQualifiedName(Solution solution, DocumentId do return null; } - var remoteHostClient = await solution.Workspace.Services.GetService().GetRemoteHostClientAsync(cancellationToken).ConfigureAwait(false); + var remoteHostClient = await solution.Workspace.Services.GetService().TryGetRemoteHostClientAsync(cancellationToken).ConfigureAwait(false); if (remoteHostClient == null) { // remote host is not running. this can happen if remote host is disabled. diff --git a/src/VisualStudio/Core/Test.Next/Services/VisualStudioDiagnosticAnalyzerExecutorTests.cs b/src/VisualStudio/Core/Test.Next/Services/VisualStudioDiagnosticAnalyzerExecutorTests.cs index 8f9b992a9480..3fb35eb1332f 100644 --- a/src/VisualStudio/Core/Test.Next/Services/VisualStudioDiagnosticAnalyzerExecutorTests.cs +++ b/src/VisualStudio/Core/Test.Next/Services/VisualStudioDiagnosticAnalyzerExecutorTests.cs @@ -145,7 +145,7 @@ void Method() var asset = assetBuilder.Build(analyzerReference, CancellationToken.None); snapshotService.AddGlobalAsset(analyzerReference, asset, CancellationToken.None); - var client = await workspace.Services.GetService().GetRemoteHostClientAsync(CancellationToken.None); + var client = await workspace.Services.GetService().TryGetRemoteHostClientAsync(CancellationToken.None); await client.RunOnRemoteHostAsync( WellKnownRemoteHostServices.RemoteHostService, workspace.CurrentSolution, nameof(IRemoteHostService.SynchronizeGlobalAssetsAsync), (object)(new Checksum[] { asset.Checksum }), CancellationToken.None); diff --git a/src/VisualStudio/Razor/RazorLanguageServiceClientFactory.cs b/src/VisualStudio/Razor/RazorLanguageServiceClientFactory.cs index 3bd21c758463..1dc4cc8683a8 100644 --- a/src/VisualStudio/Razor/RazorLanguageServiceClientFactory.cs +++ b/src/VisualStudio/Razor/RazorLanguageServiceClientFactory.cs @@ -12,7 +12,7 @@ internal static class RazorLanguageServiceClientFactory public static async Task CreateAsync(Workspace workspace, CancellationToken cancellationToken = default(CancellationToken)) { var clientFactory = workspace.Services.GetRequiredService(); - var client = await clientFactory.GetRemoteHostClientAsync(cancellationToken).ConfigureAwait(false); + var client = await clientFactory.TryGetRemoteHostClientAsync(cancellationToken).ConfigureAwait(false); return client == null ? null : new RazorLangaugeServiceClient(client); } } diff --git a/src/Workspaces/Core/Desktop/SymbolSearch/SymbolSearchUpdateEngineFactory.cs b/src/Workspaces/Core/Desktop/SymbolSearch/SymbolSearchUpdateEngineFactory.cs index e0b6c13106fb..dbf289fb1a9a 100644 --- a/src/Workspaces/Core/Desktop/SymbolSearch/SymbolSearchUpdateEngineFactory.cs +++ b/src/Workspaces/Core/Desktop/SymbolSearch/SymbolSearchUpdateEngineFactory.cs @@ -22,7 +22,7 @@ public static async Task CreateEngineAsync( var outOfProcessAllowed = workspace.Options.GetOption(SymbolSearchOptions.OutOfProcessAllowed); if (outOfProcessAllowed) { - var client = await workspace.GetRemoteHostClientAsync(cancellationToken).ConfigureAwait(false); + var client = await workspace.TryGetRemoteHostClientAsync(cancellationToken).ConfigureAwait(false); if (client != null) { return new RemoteUpdateEngine(workspace, client, logService, cancellationToken); @@ -75,7 +75,7 @@ private async void OnConnectionChanged(object sender, bool connected) _sessionDoNotAccessDirectly?.Dispose(); _sessionDoNotAccessDirectly = null; - _client = await _workspace.GetRemoteHostClientAsync(CancellationToken.None).ConfigureAwait(false); + _client = await _workspace.TryGetRemoteHostClientAsync(CancellationToken.None).ConfigureAwait(false); if (_client != null) { // client can be null if host is shutting down diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_Remote.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_Remote.cs index 44e09e272e58..3af943376df7 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_Remote.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_Remote.cs @@ -80,7 +80,7 @@ private static async Task FindReferencesInServiceProcessAsync( IImmutableSet documents, CancellationToken cancellationToken) { - var client = await solution.Workspace.GetRemoteHostClientAsync(cancellationToken).ConfigureAwait(false); + var client = await solution.Workspace.TryGetRemoteHostClientAsync(cancellationToken).ConfigureAwait(false); if (client == null) { await FindReferencesInCurrentProcessAsync( @@ -133,7 +133,7 @@ private static async Task TryFindLiteralReferencesInServiceProcessAsync( IStreamingFindLiteralReferencesProgress progress, CancellationToken cancellationToken) { - var client = await solution.Workspace.GetRemoteHostClientAsync(cancellationToken).ConfigureAwait(false); + var client = await solution.Workspace.TryGetRemoteHostClientAsync(cancellationToken).ConfigureAwait(false); if (client == null) { return false; diff --git a/src/Workspaces/Core/Portable/Remote/DefaultRemoteHostClientServiceFactory.RemoteHostClientService.cs b/src/Workspaces/Core/Portable/Remote/DefaultRemoteHostClientServiceFactory.RemoteHostClientService.cs index 0e3e62fc5a27..efb4b7b2fbfc 100644 --- a/src/Workspaces/Core/Portable/Remote/DefaultRemoteHostClientServiceFactory.RemoteHostClientService.cs +++ b/src/Workspaces/Core/Portable/Remote/DefaultRemoteHostClientServiceFactory.RemoteHostClientService.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using System.Threading; using System.Threading.Tasks; using Roslyn.Utilities; @@ -25,6 +26,11 @@ public RemoteHostClientService(Workspace workspace) } public Task GetRemoteHostClientAsync(CancellationToken cancellationToken) + { + return TryGetRemoteHostClientAsync(cancellationToken); + } + + public Task TryGetRemoteHostClientAsync(CancellationToken cancellationToken) { if (_lazyInstance == null) { diff --git a/src/Workspaces/Core/Portable/Remote/IRemoteHostClientService.cs b/src/Workspaces/Core/Portable/Remote/IRemoteHostClientService.cs index e65a6fa4c607..3162e83453b9 100644 --- a/src/Workspaces/Core/Portable/Remote/IRemoteHostClientService.cs +++ b/src/Workspaces/Core/Portable/Remote/IRemoteHostClientService.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Host; @@ -11,6 +12,8 @@ namespace Microsoft.CodeAnalysis.Remote /// internal interface IRemoteHostClientService : IWorkspaceService { + [Obsolete("use TryGetRemoteHostClientAsync instead")] Task GetRemoteHostClientAsync(CancellationToken cancellationToken); + Task TryGetRemoteHostClientAsync(CancellationToken cancellationToken); } } diff --git a/src/Workspaces/Core/Portable/Remote/RemoteHostClientExtensions.cs b/src/Workspaces/Core/Portable/Remote/RemoteHostClientExtensions.cs index e39f74d89d26..b7089c9e5f3d 100644 --- a/src/Workspaces/Core/Portable/Remote/RemoteHostClientExtensions.cs +++ b/src/Workspaces/Core/Portable/Remote/RemoteHostClientExtensions.cs @@ -37,10 +37,10 @@ internal static class RemoteHostClientExtensions WellKnownServiceHubServices.CodeAnalysisService, solution, callbackTarget, cancellationToken); } - public static Task GetRemoteHostClientAsync(this Workspace workspace, CancellationToken cancellationToken) + public static Task TryGetRemoteHostClientAsync(this Workspace workspace, CancellationToken cancellationToken) { var clientService = workspace.Services.GetService(); - return clientService?.GetRemoteHostClientAsync(cancellationToken); + return clientService?.TryGetRemoteHostClientAsync(cancellationToken); } public static Task RunOnRemoteHostAsync(