未验证 提交 9f2acc77 编写于 作者: T Tomáš Matoušek 提交者: GitHub

Remote client service refactoring (#44322)

* Remove remote host restarting logic

* RemoteServiceName

* Remove dependency on Workspace

* Refactoring

* Fixes
上级 14a785e3
......@@ -31,7 +31,7 @@ internal static partial class SymbolSearchUpdateEngineFactory
if (client != null)
{
var callbackObject = new CallbackObject(logService, progressService);
var session = await client.TryCreateKeepAliveSessionAsync(WellKnownServiceHubServices.RemoteSymbolSearchUpdateEngine, callbackObject, cancellationToken).ConfigureAwait(false);
var session = await client.TryCreateKeepAliveSessionAsync(WellKnownServiceHubService.RemoteSymbolSearchUpdateEngine, callbackObject, cancellationToken).ConfigureAwait(false);
if (session != null)
{
return new RemoteUpdateEngine(workspace, session);
......
......@@ -53,7 +53,7 @@ public async Task FindImplementationsAsync(Document document, int position, IFin
var serverCallback = new FindUsagesServerCallback(solution, context);
var success = await client.TryRunRemoteAsync(
WellKnownServiceHubServices.CodeAnalysisService,
WellKnownServiceHubService.CodeAnalysis,
nameof(IRemoteFindUsagesService.FindImplementationsAsync),
solution,
new object[]
......
......@@ -140,7 +140,7 @@ internal abstract partial class AbstractFindUsagesService
var serverCallback = new FindUsagesServerCallback(solution, context);
var success = await client.TryRunRemoteAsync(
WellKnownServiceHubServices.CodeAnalysisService,
WellKnownServiceHubService.CodeAnalysis,
nameof(IRemoteFindUsagesService.FindReferencesAsync),
solution,
new object[]
......
......@@ -7,6 +7,8 @@
using System.Composition;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Execution;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Options.Providers;
......@@ -44,11 +46,12 @@ public InProcRemoteHostClientFactory()
{
}
public Task<RemoteHostClient> CreateAsync(Workspace workspace, CancellationToken cancellationToken)
public Task<RemoteHostClient> CreateAsync(HostWorkspaceServices services, CancellationToken cancellationToken)
{
if (workspace.Options.GetOption(RemoteHostOptions.RemoteHostTest))
var optionService = services.GetRequiredService<IOptionService>();
if (optionService.GetOption(RemoteHostOptions.RemoteHostTest))
{
return InProcRemoteHostClient.CreateAsync(workspace, runCacheCleanup: false);
return InProcRemoteHostClient.CreateAsync(services, runCacheCleanup: false);
}
return SpecializedTasks.Null<RemoteHostClient>();
......
......@@ -14,6 +14,7 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Execution;
using Microsoft.CodeAnalysis.Experiments;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Remote;
using Microsoft.VisualStudio.LanguageServices.Remote;
using Nerdbank;
......@@ -27,14 +28,14 @@ internal sealed class InProcRemoteHostClient : RemoteHostClient, IRemoteHostServ
private readonly InProcRemoteServices _inprocServices;
private readonly RemoteEndPoint _endPoint;
public static async Task<RemoteHostClient> CreateAsync(Workspace workspace, bool runCacheCleanup)
public static async Task<RemoteHostClient> CreateAsync(HostWorkspaceServices services, bool runCacheCleanup)
{
var inprocServices = new InProcRemoteServices(runCacheCleanup);
var remoteHostStream = await inprocServices.RequestServiceAsync(WellKnownServiceHubServices.RemoteHostService).ConfigureAwait(false);
var remoteHostStream = await inprocServices.RequestServiceAsync(WellKnownServiceHubService.RemoteHost).ConfigureAwait(false);
var current = CreateClientId(Process.GetCurrentProcess().Id.ToString());
var instance = new InProcRemoteHostClient(current, workspace, inprocServices, remoteHostStream);
var instance = new InProcRemoteHostClient(current, services, inprocServices, remoteHostStream);
// make sure connection is done right
string? telemetrySession = null;
......@@ -57,10 +58,10 @@ public static async Task<RemoteHostClient> CreateAsync(Workspace workspace, bool
private InProcRemoteHostClient(
string clientId,
Workspace workspace,
HostWorkspaceServices services,
InProcRemoteServices inprocServices,
Stream stream)
: base(workspace)
: base(services)
{
ClientId = clientId;
......@@ -85,27 +86,26 @@ public Task GetAssetsAsync(int scopeId, Checksum[] checksums, string pipeName, C
/// Remote API.
/// </summary>
public Task<bool> IsExperimentEnabledAsync(string experimentName, CancellationToken cancellationToken)
=> Task.FromResult(Workspace.Services.GetRequiredService<IExperimentationService>().IsExperimentEnabled(experimentName));
=> Task.FromResult(Services.GetRequiredService<IExperimentationService>().IsExperimentEnabled(experimentName));
public AssetStorage AssetStorage => _inprocServices.AssetStorage;
public void RegisterService(string name, Func<Stream, IServiceProvider, ServiceBase> serviceCreator)
=> _inprocServices.RegisterService(name, serviceCreator);
public void RegisterService(RemoteServiceName serviceName, Func<Stream, IServiceProvider, ServiceBase> serviceCreator)
=> _inprocServices.RegisterService(serviceName, serviceCreator);
public Task<Stream> RequestServiceAsync(string serviceName)
public Task<Stream> RequestServiceAsync(RemoteServiceName serviceName)
=> _inprocServices.RequestServiceAsync(serviceName);
public override string ClientId { get; }
public override bool IsRemoteHost64Bit => IntPtr.Size == 8;
protected override async Task<Connection?> TryCreateConnectionAsync(
string serviceName, object? callbackTarget, CancellationToken cancellationToken)
RemoteServiceName serviceName, object? callbackTarget, CancellationToken cancellationToken)
{
// get stream from service hub to communicate service specific information
// this is what consumer actually use to communicate information
var serviceStream = await _inprocServices.RequestServiceAsync(serviceName).ConfigureAwait(false);
return new JsonRpcConnection(Workspace, _inprocServices.Logger, callbackTarget, serviceStream);
return new JsonRpcConnection(Services, _inprocServices.Logger, callbackTarget, serviceStream);
}
public override void Dispose()
......@@ -154,29 +154,29 @@ public object GetService(Type serviceType)
private class InProcRemoteServices
{
private readonly ServiceProvider _serviceProvider;
private readonly Dictionary<string, Func<Stream, IServiceProvider, ServiceBase>> _creatorMap;
private readonly Dictionary<RemoteServiceName, Func<Stream, IServiceProvider, ServiceBase>> _creatorMap;
public InProcRemoteServices(bool runCacheCleanup)
{
_serviceProvider = new ServiceProvider(runCacheCleanup);
_creatorMap = new Dictionary<string, Func<Stream, IServiceProvider, ServiceBase>>();
RegisterService(WellKnownServiceHubServices.RemoteHostService, (s, p) => new RemoteHostService(s, p));
RegisterService(WellKnownServiceHubServices.CodeAnalysisService, (s, p) => new CodeAnalysisService(s, p));
RegisterService(WellKnownServiceHubServices.RemoteSymbolSearchUpdateEngine, (s, p) => new RemoteSymbolSearchUpdateEngine(s, p));
RegisterService(WellKnownServiceHubServices.RemoteDesignerAttributeService, (s, p) => new RemoteDesignerAttributeService(s, p));
RegisterService(WellKnownServiceHubServices.RemoteProjectTelemetryService, (s, p) => new RemoteProjectTelemetryService(s, p));
RegisterService(WellKnownServiceHubServices.RemoteTodoCommentsService, (s, p) => new RemoteTodoCommentsService(s, p));
RegisterService(WellKnownServiceHubServices.LanguageServer, (s, p) => new LanguageServer(s, p));
_creatorMap = new Dictionary<RemoteServiceName, Func<Stream, IServiceProvider, ServiceBase>>();
RegisterService(WellKnownServiceHubService.RemoteHost, (s, p) => new RemoteHostService(s, p));
RegisterService(WellKnownServiceHubService.CodeAnalysis, (s, p) => new CodeAnalysisService(s, p));
RegisterService(WellKnownServiceHubService.RemoteSymbolSearchUpdateEngine, (s, p) => new RemoteSymbolSearchUpdateEngine(s, p));
RegisterService(WellKnownServiceHubService.RemoteDesignerAttributeService, (s, p) => new RemoteDesignerAttributeService(s, p));
RegisterService(WellKnownServiceHubService.RemoteProjectTelemetryService, (s, p) => new RemoteProjectTelemetryService(s, p));
RegisterService(WellKnownServiceHubService.RemoteTodoCommentsService, (s, p) => new RemoteTodoCommentsService(s, p));
RegisterService(WellKnownServiceHubService.LanguageServer, (s, p) => new LanguageServer(s, p));
}
public AssetStorage AssetStorage => _serviceProvider.AssetStorage;
public TraceSource Logger { get; } = new TraceSource("Default");
public void RegisterService(string name, Func<Stream, IServiceProvider, ServiceBase> serviceCreator)
public void RegisterService(RemoteServiceName name, Func<Stream, IServiceProvider, ServiceBase> serviceCreator)
=> _creatorMap.Add(name, serviceCreator);
public Task<Stream> RequestServiceAsync(string serviceName)
public Task<Stream> RequestServiceAsync(RemoteServiceName serviceName)
{
if (_creatorMap.TryGetValue(serviceName, out var creator))
{
......
......@@ -4,6 +4,7 @@
using System;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Host;
namespace Microsoft.VisualStudio.LanguageServices.Remote
{
......@@ -12,7 +13,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Remote
/// </summary>
internal static class RemoteHostCrashInfoBar
{
public static void ShowInfoBar(Workspace workspace, Exception ex = null)
public static void ShowInfoBar(HostWorkspaceServices services, Exception ex = null)
{
// do nothing
}
......
......@@ -60,7 +60,7 @@ internal abstract partial class AbstractAddImportFeatureService<TSimpleNameSynta
var callbackTarget = new RemoteSymbolSearchService(symbolSearchService);
var result = await client.TryRunRemoteAsync<IList<AddImportFixData>>(
WellKnownServiceHubServices.CodeAnalysisService,
WellKnownServiceHubService.CodeAnalysis,
nameof(IRemoteAddImportFeatureService.GetFixesAsync),
document.Project.Solution,
new object[]
......
......@@ -45,7 +45,7 @@ async Task<(ImmutableArray<SerializableImportCompletionItem>, StatisticCounter)>
if (client != null)
{
var result = await client.TryRunRemoteAsync<(IList<SerializableImportCompletionItem> items, StatisticCounter counter)>(
WellKnownServiceHubServices.CodeAnalysisService,
WellKnownServiceHubService.CodeAnalysis,
nameof(IRemoteExtensionMethodImportCompletionService.GetUnimportedExtensionMethodsAsync),
project.Solution,
new object[] { document.Id, position, SymbolKey.CreateString(receiverTypeSymbol), namespaceInScope.ToArray(), forceIndexCreation },
......
......@@ -168,7 +168,7 @@ private static string GetTitle(Scope scope)
if (client != null)
{
var resultOpt = await client.TryRunRemoteAsync<SerializableConvertTupleToStructResult>(
WellKnownServiceHubServices.CodeAnalysisService,
WellKnownServiceHubService.CodeAnalysis,
nameof(IRemoteConvertTupleToStructCodeRefactoringProvider.ConvertToStructAsync),
solution,
new object[]
......
......@@ -92,7 +92,7 @@ private async Task FireAndForgetReportAnalyzerPerformanceAsync(Project project,
try
{
_ = await client.TryRunRemoteAsync(
WellKnownServiceHubServices.CodeAnalysisService,
WellKnownServiceHubService.CodeAnalysis,
nameof(IRemoteDiagnosticAnalyzerService.ReportAnalyzerPerformance),
solution: null,
new object[]
......@@ -129,7 +129,7 @@ private async Task FireAndForgetReportAnalyzerPerformanceAsync(Project project,
project.Id, analyzerMap.Keys.ToArray());
var result = await client.TryRunRemoteAsync(
WellKnownServiceHubServices.CodeAnalysisService,
WellKnownServiceHubService.CodeAnalysis,
nameof(IRemoteDiagnosticAnalyzerService.CalculateDiagnosticsAsync),
solution,
new object[] { argument },
......
......@@ -546,7 +546,7 @@ private async Task ReportAnalyzerPerformanceAsync(Document document, Compilation
}
_ = await client.TryRunRemoteAsync(
WellKnownServiceHubServices.CodeAnalysisService,
WellKnownServiceHubService.CodeAnalysis,
nameof(IRemoteDiagnosticAnalyzerService.ReportAnalyzerPerformance),
solution: null,
new object[] { pooledObject.Object.ToAnalyzerPerformanceInfo(DiagnosticAnalyzerInfoCache), /* unit count */ 1 },
......
......@@ -31,7 +31,7 @@ internal abstract partial class AbstractDocumentHighlightsService : IDocumentHig
if (client != null)
{
var result = await client.TryRunRemoteAsync<IList<SerializableDocumentHighlights>>(
WellKnownServiceHubServices.CodeAnalysisService,
WellKnownServiceHubService.CodeAnalysis,
nameof(IRemoteDocumentHighlights.GetDocumentHighlightsAsync),
solution,
new object[]
......
......@@ -105,7 +105,7 @@ private ImmutableArray<CodeAction> EncapsulateOneField(Document document, IField
if (client != null)
{
var result = await client.TryRunRemoteAsync<(DocumentId, TextChange[])[]>(
WellKnownServiceHubServices.CodeAnalysisService,
WellKnownServiceHubService.CodeAnalysis,
nameof(IRemoteEncapsulateFieldService.EncapsulateFieldsAsync),
solution,
new object[]
......
......@@ -39,7 +39,7 @@ internal abstract partial class AbstractNavigateToSearchService : INavigateToSea
var solution = document.Project.Solution;
var result = await client.TryRunRemoteAsync<IList<SerializableNavigateToSearchResult>>(
WellKnownServiceHubServices.CodeAnalysisService,
WellKnownServiceHubService.CodeAnalysis,
nameof(IRemoteNavigateToSearchService.SearchDocumentAsync),
solution,
new object[] { document.Id, searchPattern, kinds.ToArray() },
......@@ -65,7 +65,7 @@ internal abstract partial class AbstractNavigateToSearchService : INavigateToSea
var solution = project.Solution;
var result = await client.TryRunRemoteAsync<IList<SerializableNavigateToSearchResult>>(
WellKnownServiceHubServices.CodeAnalysisService,
WellKnownServiceHubService.CodeAnalysis,
nameof(IRemoteNavigateToSearchService.SearchProjectAsync),
solution,
new object[] { project.Id, priorityDocuments.Select(d => d.Id).ToArray(), searchPattern, kinds.ToArray() },
......
......@@ -14,55 +14,20 @@ namespace Microsoft.CodeAnalysis.ExternalAccess.Razor
internal sealed class RazorRemoteHostClient
{
private readonly RemoteHostClient _client;
private readonly string _serviceName;
internal RazorRemoteHostClient(RemoteHostClient client, string serviceName)
internal RazorRemoteHostClient(RemoteHostClient client)
{
_client = client;
_serviceName = serviceName;
}
public static async Task<RazorRemoteHostClient?> CreateAsync(Workspace workspace, CancellationToken cancellationToken = default)
{
var clientFactory = workspace.Services.GetRequiredService<IRemoteHostClientService>();
var client = await clientFactory.TryGetRemoteHostClientAsync(cancellationToken).ConfigureAwait(false);
return client == null ? null : new RazorRemoteHostClient(client, GetServiceName(workspace));
return client == null ? null : new RazorRemoteHostClient(client);
}
public Task<Optional<T>> TryRunRemoteAsync<T>(string targetName, Solution? solution, IReadOnlyList<object?> arguments, CancellationToken cancellationToken)
=> _client.TryRunRemoteAsync<T>(_serviceName, targetName, solution, arguments, callbackTarget: null, cancellationToken);
#region support a/b testing. after a/b testing, we can remove all this code
private static string? s_lazyServiceName = null;
private static string GetServiceName(Workspace workspace)
{
if (s_lazyServiceName == null)
{
var x64 = workspace.Options.GetOption(OOP64Bit);
if (!x64)
{
x64 = workspace.Services.GetRequiredService<IExperimentationService>().IsExperimentEnabled(
WellKnownExperimentNames.RoslynOOP64bit);
}
Interlocked.CompareExchange(
ref s_lazyServiceName, x64 ? "razorLanguageService64" : "razorLanguageService", null);
}
return s_lazyServiceName;
}
public static readonly Option<bool> OOP64Bit = new Option<bool>(
nameof(InternalFeatureOnOffOptions), nameof(OOP64Bit), defaultValue: false,
storageLocations: new LocalUserProfileStorageLocation(InternalFeatureOnOffOptions.LocalRegistryPath + nameof(OOP64Bit)));
private static class InternalFeatureOnOffOptions
{
internal const string LocalRegistryPath = @"Roslyn\Internal\OnOff\Features\";
}
#endregion
=> _client.TryRunRemoteAsync<T>(WellKnownServiceHubService.Razor, targetName, solution, arguments, callbackTarget: null, cancellationToken);
}
}
......@@ -41,7 +41,7 @@ public RemoteCodeLensReferencesService()
if (client != null)
{
var result = await client.TryRunRemoteAsync<ReferenceCount>(
WellKnownServiceHubServices.CodeAnalysisService,
WellKnownServiceHubService.CodeAnalysis,
nameof(IRemoteCodeLensReferencesService.GetReferenceCountAsync),
solution,
new object[] { documentId, syntaxNode.Span, maxSearchResults },
......@@ -88,7 +88,7 @@ public RemoteCodeLensReferencesService()
if (client != null)
{
var result = await client.TryRunRemoteAsync<IEnumerable<ReferenceMethodDescriptor>>(
WellKnownServiceHubServices.CodeAnalysisService,
WellKnownServiceHubService.CodeAnalysis,
nameof(IRemoteCodeLensReferencesService.FindReferenceMethodsAsync),
solution,
new object[] { documentId, syntaxNode.Span },
......@@ -119,7 +119,7 @@ public RemoteCodeLensReferencesService()
if (client != null)
{
var result = await client.TryRunRemoteAsync<string>(
WellKnownServiceHubServices.CodeAnalysisService,
WellKnownServiceHubService.CodeAnalysis,
nameof(IRemoteCodeLensReferencesService.GetFullyQualifiedNameAsync),
solution,
new object[] { documentId, syntaxNode.Span },
......@@ -253,7 +253,7 @@ private static string GetLineTextOrEmpty(TextLineCollection lines, int index)
if (client != null)
{
var result = await client.TryRunRemoteAsync<IEnumerable<ReferenceLocationDescriptor>>(
WellKnownServiceHubServices.CodeAnalysisService,
WellKnownServiceHubService.CodeAnalysis,
nameof(IRemoteCodeLensReferencesService.FindReferenceLocationsAsync),
solution,
new object[] { documentId, syntaxNode.Span },
......
......@@ -119,7 +119,7 @@ private async Task StartWorkerAsync(CancellationToken cancellationToken)
// Pass ourselves in as the callback target for the OOP service. As it discovers
// designer attributes it will call back into us to notify VS about it.
_keepAliveSession = await client.TryCreateKeepAliveSessionAsync(
WellKnownServiceHubServices.RemoteDesignerAttributeService,
WellKnownServiceHubService.RemoteDesignerAttributeService,
callbackTarget: this, cancellationToken).ConfigureAwait(false);
if (_keepAliveSession == null)
return;
......
......@@ -12,6 +12,7 @@
using Microsoft.CodeAnalysis.Editor;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.Experiments;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Internal.Log;
using Microsoft.CodeAnalysis.Options;
......@@ -33,7 +34,7 @@ internal sealed class LanguageServerClient : ILanguageClient
private const string ServiceHubClientName = "ManagedLanguage.IDE.LanguageServer";
private readonly IThreadingContext _threadingContext;
private readonly Workspace _workspace;
private readonly HostWorkspaceServices _services;
private readonly IEnumerable<Lazy<IOptionPersister>> _lazyOptions;
/// <summary>
......@@ -71,13 +72,13 @@ internal sealed class LanguageServerClient : ILanguageClient
[ImportMany] IEnumerable<Lazy<IOptionPersister>> lazyOptions)
{
_threadingContext = threadingContext;
_workspace = workspace;
_services = workspace.Services;
_lazyOptions = lazyOptions;
}
public async Task<Connection> ActivateAsync(CancellationToken cancellationToken)
{
var client = await RemoteHostClient.TryGetClientAsync(_workspace, cancellationToken).ConfigureAwait(false);
var client = await RemoteHostClient.TryGetClientAsync(_services, cancellationToken).ConfigureAwait(false);
if (client == null)
{
// There is no OOP. either user turned it off, or process got killed.
......@@ -91,9 +92,9 @@ public async Task<Connection> ActivateAsync(CancellationToken cancellationToken)
var hubClient = new HubClient(ServiceHubClientName);
var stream = await ServiceHubRemoteHostClient.RequestServiceAsync(
_workspace,
_services,
hubClient,
WellKnownServiceHubServices.LanguageServer,
WellKnownServiceHubService.LanguageServer,
hostGroup,
cancellationToken).ConfigureAwait(false);
......@@ -113,10 +114,10 @@ public async Task OnLoadedAsync()
// this might get called before solution is fully loaded and before file is opened.
// we delay our OOP start until then, but user might do vsstart before that. so we make sure we start OOP if
// it is not running yet. multiple start is no-op
((RemoteHostClientServiceFactory.RemoteHostClientService)_workspace.Services.GetService<IRemoteHostClientService>()).Enable();
((RemoteHostClientServiceFactory.RemoteHostClientService)_services.GetService<IRemoteHostClientService>()).Enable();
// wait until remote host is available before let platform know that they can activate our LSP
var client = await RemoteHostClient.TryGetClientAsync(_workspace, CancellationToken.None).ConfigureAwait(false);
var client = await RemoteHostClient.TryGetClientAsync(_services, CancellationToken.None).ConfigureAwait(false);
if (client == null)
{
// There is no OOP. either user turned it off, or process got killed.
......@@ -146,7 +147,7 @@ async Task InitializeOnUIAsync()
// experimentation service unfortunately uses JTF to jump to UI thread in certain cases
// which can cause deadlock if 2 parties try to enable OOP from BG and then FG before
// experimentation service tries to jump to UI thread.
var experimentationService = _workspace.Services.GetService<IExperimentationService>();
var experimentationService = _services.GetService<IExperimentationService>();
}
}
......
......@@ -96,7 +96,7 @@ private async Task StartWorkerAsync(CancellationToken cancellationToken)
// Pass ourselves in as the callback target for the OOP service. As it discovers
// designer attributes it will call back into us to notify VS about it.
_keepAliveSession = await client.TryCreateKeepAliveSessionAsync(
WellKnownServiceHubServices.RemoteProjectTelemetryService,
WellKnownServiceHubService.RemoteProjectTelemetryService,
callbackTarget: this, cancellationToken).ConfigureAwait(false);
if (_keepAliveSession == null)
return;
......
......@@ -96,7 +96,7 @@ private async Task<GlobalNotificationState> SendStartNotificationAsync(Task<Glob
}
_ = await client.TryRunRemoteAsync(
WellKnownServiceHubServices.CodeAnalysisService,
WellKnownServiceHubService.CodeAnalysis,
nameof(IRemoteGlobalNotificationDeliveryService.OnGlobalOperationStarted),
solution: null,
Array.Empty<object>(),
......@@ -131,7 +131,7 @@ private async Task<GlobalNotificationState> SendStoppedNotificationAsync(Task<Gl
}
_ = await client.TryRunRemoteAsync(
WellKnownServiceHubServices.CodeAnalysisService,
WellKnownServiceHubService.CodeAnalysis,
nameof(IRemoteGlobalNotificationDeliveryService.OnGlobalOperationStopped),
solution: null,
new object[] { e.Operations, e.Cancelled },
......
......@@ -11,31 +11,32 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Remote;
namespace Microsoft.VisualStudio.LanguageServices.Remote
{
internal class JsonRpcConnection : RemoteHostClient.Connection
{
private readonly Workspace _workspace;
private readonly HostWorkspaceServices _services;
// communication channel related to service information
private readonly RemoteEndPoint _serviceEndPoint;
public JsonRpcConnection(
Workspace workspace,
HostWorkspaceServices services,
TraceSource logger,
object? callbackTarget,
Stream serviceStream)
{
_workspace = workspace;
_services = services;
_serviceEndPoint = new RemoteEndPoint(serviceStream, logger, callbackTarget);
_serviceEndPoint.UnexpectedExceptionThrown += UnexpectedExceptionThrown;
_serviceEndPoint.StartListening();
}
private void UnexpectedExceptionThrown(Exception exception)
=> RemoteHostCrashInfoBar.ShowInfoBar(_workspace, exception);
=> RemoteHostCrashInfoBar.ShowInfoBar(_services, exception);
public override Task InvokeAsync(string targetName, IReadOnlyList<object?> arguments, CancellationToken cancellationToken)
=> _serviceEndPoint.InvokeAsync(targetName, arguments, cancellationToken);
......
......@@ -9,6 +9,7 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Remote;
......@@ -23,10 +24,10 @@ public RemoteHostClientFactory()
{
}
public async Task<RemoteHostClient?> CreateAsync(Workspace workspace, CancellationToken cancellationToken)
public async Task<RemoteHostClient?> CreateAsync(HostWorkspaceServices services, CancellationToken cancellationToken)
{
// this is the point where we can create different kind of remote host client in future (cloud or etc)
return await ServiceHubRemoteHostClient.CreateAsync(workspace, cancellationToken).ConfigureAwait(false);
return await ServiceHubRemoteHostClient.CreateAsync(services, cancellationToken).ConfigureAwait(false);
}
}
}
......@@ -11,6 +11,7 @@
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.ErrorReporting;
using Microsoft.CodeAnalysis.Internal.Log;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Remote;
using Microsoft.CodeAnalysis.Shared.TestHooks;
using Roslyn.Utilities;
......@@ -63,8 +64,9 @@ public void Enable()
}
// We enable the remote host if either RemoteHostTest or RemoteHost are on.
if (!_workspace.Options.GetOption(RemoteHostOptions.RemoteHostTest) &&
!_workspace.Options.GetOption(RemoteHostOptions.RemoteHost))
var optionService = _workspace.Services.GetRequiredService<IOptionService>();
if (!optionService.GetOption(RemoteHostOptions.RemoteHostTest) &&
!optionService.GetOption(RemoteHostOptions.RemoteHost))
{
// not turned on
return;
......@@ -72,6 +74,7 @@ public void Enable()
// log that remote host is enabled
Logger.Log(FunctionId.RemoteHostClientService_Enabled, KeyValueLogMessage.NoProperty);
Logger.Log(FunctionId.RemoteHost_Bitness, KeyValueLogMessage.Create(LogType.Trace, m => m["64bit"] = RemoteHostOptions.IsServiceHubProcess64Bit(_workspace.Services)));
var remoteHostClientFactory = _workspace.Services.GetService<IRemoteHostClientFactory>();
if (remoteHostClientFactory == null)
......@@ -80,9 +83,6 @@ public void Enable()
return;
}
// set bitness
SetRemoteHostBitness();
// make sure we run it on background thread
_shutdownCancellationTokenSource = new CancellationTokenSource();
......@@ -148,8 +148,9 @@ public void Disable()
bool IRemoteHostClientService.IsEnabled()
{
// We enable the remote host if either RemoteHostTest or RemoteHost are on.
if (!_workspace.Options.GetOption(RemoteHostOptions.RemoteHostTest)
&& !_workspace.Options.GetOption(RemoteHostOptions.RemoteHost))
var optionService = _workspace.Services.GetRequiredService<IOptionService>();
if (!optionService.GetOption(RemoteHostOptions.RemoteHostTest)
&& !optionService.GetOption(RemoteHostOptions.RemoteHost))
{
// not turned on
return false;
......@@ -184,22 +185,11 @@ bool IRemoteHostClientService.IsEnabled()
return remoteClientTask;
}
private void SetRemoteHostBitness()
{
bool x64 = RemoteHostOptions.IsServiceHubProcess64Bit(_workspace);
// log OOP bitness
Logger.Log(FunctionId.RemoteHost_Bitness, KeyValueLogMessage.Create(LogType.Trace, m => m["64bit"] = x64));
// set service bitness
WellKnownServiceHubServices.Set64bit(x64);
}
private async Task<RemoteHostClient?> EnableAsync(CancellationToken cancellationToken)
{
// if we reached here, IRemoteHostClientFactory must exist.
// this will make VS.Next dll to be loaded
var client = await _workspace.Services.GetRequiredService<IRemoteHostClientFactory>().CreateAsync(_workspace, cancellationToken).ConfigureAwait(false);
var client = await _workspace.Services.GetRequiredService<IRemoteHostClientFactory>().CreateAsync(_workspace.Services, cancellationToken).ConfigureAwait(false);
if (client != null)
{
client.StatusChanged += OnStatusChanged;
......@@ -235,42 +225,15 @@ private void OnStatusChanged(object sender, bool started)
// save NoOpRemoteHostClient to remoteClient so that all RemoteHost call becomes
// No Op. this basically have same effect as disabling all RemoteHost features
_remoteClientTask = Task.FromResult<RemoteHostClient?>(new RemoteHostClient.NoOpClient(_workspace));
_remoteClientTask = Task.FromResult<RemoteHostClient?>(new RemoteHostClient.NoOpClient(_workspace.Services));
}
// s_lastRemoteClientTask info should be saved in the dump
// report NFW when connection is closed unless it is proper shutdown
FatalError.ReportWithoutCrash(new InvalidOperationException("Connection to remote host closed"));
RemoteHostCrashInfoBar.ShowInfoBar(_workspace);
}
}
public async Task RequestNewRemoteHostAsync(CancellationToken cancellationToken)
{
var existingClient = await TryGetRemoteHostClientAsync(cancellationToken).ConfigureAwait(false);
if (existingClient == null)
{
return;
RemoteHostCrashInfoBar.ShowInfoBar(_workspace.Services);
}
Contract.ThrowIfNull(_shutdownCancellationTokenSource);
// log that remote host is restarted
Logger.Log(FunctionId.RemoteHostClientService_Restarted, KeyValueLogMessage.NoProperty);
// we are going to kill the existing remote host, connection change is expected
existingClient.StatusChanged -= OnStatusChanged;
lock (_gate)
{
// create new remote host client
var token = _shutdownCancellationTokenSource.Token;
_remoteClientTask = Task.Run(() => EnableAsync(token), token);
}
// shutdown
existingClient.Dispose();
}
}
}
......
......@@ -2,12 +2,13 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#nullable enable
using System;
using System.Collections.Generic;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Extensions;
using Microsoft.CodeAnalysis.Remote;
using Microsoft.CodeAnalysis.Host;
using Microsoft.VisualStudio.LanguageServices.Implementation.Utilities;
namespace Microsoft.VisualStudio.LanguageServices.Remote
......@@ -22,10 +23,9 @@ internal static class RemoteHostCrashInfoBar
private static bool s_infoBarReported = false;
public static void ShowInfoBar(Workspace workspace, Exception exception = null)
public static void ShowInfoBar(HostWorkspaceServices services, Exception? exception = null)
{
// use info bar to show warning to users
if (workspace == null || s_infoBarReported)
if (s_infoBarReported)
{
return;
}
......@@ -39,22 +39,7 @@ public static void ShowInfoBar(Workspace workspace, Exception exception = null)
new InfoBarUI(ServicesVSResources.Learn_more, InfoBarUI.UIKind.HyperLink, () =>
BrowserHelper.StartBrowser(new Uri(OOPKilledMoreInfoLink)), closeAfterAction: false));
var service = workspace.Services.GetService<IRemoteHostClientService>();
var allowRestarting = workspace.Options.GetOption(RemoteHostOptions.RestartRemoteHostAllowed);
if (allowRestarting && service != null)
{
// this is hidden restart option. by default, user can't restart remote host that got killed
// by users
infoBarUIs.Add(
new InfoBarUI("Restart external process", InfoBarUI.UIKind.Button, () =>
{
// start off new remote host
var unused = service.RequestNewRemoteHostAsync(CancellationToken.None);
s_infoBarReported = false;
}, closeAfterAction: true));
}
var errorReportingService = workspace.Services.GetRequiredService<IErrorReportingService>();
var errorReportingService = services.GetRequiredService<IErrorReportingService>();
if (exception != null)
{
......
......@@ -8,6 +8,7 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Editor.Shared.Options;
using Microsoft.CodeAnalysis.Experiments;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Options.Providers;
......@@ -35,11 +36,6 @@ internal static class RemoteHostOptions
nameof(InternalFeatureOnOffOptions), nameof(SolutionChecksumMonitorBackOffTimeSpanInMS), defaultValue: 4000,
storageLocations: new LocalUserProfileStorageLocation(InternalFeatureOnOffOptions.LocalRegistryPath + nameof(SolutionChecksumMonitorBackOffTimeSpanInMS)));
// This options allow users to restart OOP when it is killed by users
public static readonly Option<bool> RestartRemoteHostAllowed = new Option<bool>(
nameof(InternalFeatureOnOffOptions), nameof(RestartRemoteHostAllowed), defaultValue: false,
storageLocations: new LocalUserProfileStorageLocation(InternalFeatureOnOffOptions.LocalRegistryPath + nameof(RestartRemoteHostAllowed)));
// use 64bit OOP
public static readonly Option<bool> OOP64Bit = new Option<bool>(
nameof(InternalFeatureOnOffOptions), nameof(OOP64Bit), defaultValue: false,
......@@ -58,9 +54,9 @@ internal static class RemoteHostOptions
nameof(InternalFeatureOnOffOptions), nameof(MaxPoolConnection), defaultValue: 15,
storageLocations: new LocalUserProfileStorageLocation(InternalFeatureOnOffOptions.LocalRegistryPath + nameof(MaxPoolConnection)));
public static bool IsServiceHubProcess64Bit(Workspace workspace)
=> workspace.Options.GetOption(OOP64Bit) ||
workspace.Services.GetRequiredService<IExperimentationService>().IsExperimentEnabled(WellKnownExperimentNames.RoslynOOP64bit);
public static bool IsServiceHubProcess64Bit(HostWorkspaceServices services)
=> services.GetRequiredService<IOptionService>().GetOption(OOP64Bit) ||
services.GetRequiredService<IExperimentationService>().IsExperimentEnabled(WellKnownExperimentNames.RoslynOOP64bit);
}
[ExportOptionProvider, Shared]
......@@ -75,7 +71,6 @@ public RemoteHostOptionsProvider()
public ImmutableArray<IOption> Options { get; } = ImmutableArray.Create<IOption>(
RemoteHostOptions.RemoteHost,
RemoteHostOptions.SolutionChecksumMonitorBackOffTimeSpanInMS,
RemoteHostOptions.RestartRemoteHostAllowed,
RemoteHostOptions.OOP64Bit,
RemoteHostOptions.RemoteHostTest,
RemoteHostOptions.EnableConnectionPool,
......
......@@ -8,13 +8,14 @@
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Remote;
using Roslyn.Utilities;
namespace Microsoft.VisualStudio.LanguageServices.Remote
{
internal sealed partial class ServiceHubRemoteHostClient
{
private delegate Task<Connection> ConnectionFactory(string serviceName, CancellationToken cancellationToken);
private delegate Task<Connection> ConnectionFactory(RemoteServiceName serviceName, CancellationToken cancellationToken);
private sealed partial class ConnectionPool : IDisposable
{
......@@ -23,7 +24,7 @@ private sealed partial class ConnectionPool : IDisposable
private readonly int _maxPoolConnections;
// keyed to serviceName. each connection is for specific service such as CodeAnalysisService
private readonly ConcurrentDictionary<string, ConcurrentQueue<Connection>> _pools;
private readonly ConcurrentDictionary<RemoteServiceName, ConcurrentQueue<Connection>> _pools;
private bool _isDisposed;
......@@ -34,12 +35,12 @@ public ConnectionPool(ConnectionFactory connectionFactory, int maxPoolConnection
// initial value 4 is chosen to stop concurrent dictionary creating too many locks.
// and big enough for all our services such as codeanalysis, remotehost, snapshot and etc services
_pools = new ConcurrentDictionary<string, ConcurrentQueue<Connection>>(concurrencyLevel: 4, capacity: 4);
_pools = new ConcurrentDictionary<RemoteServiceName, ConcurrentQueue<Connection>>(concurrencyLevel: 4, capacity: 4);
_shutdownLock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
}
public async Task<Connection> GetOrCreateConnectionAsync(string serviceName, CancellationToken cancellationToken)
public async Task<Connection> GetOrCreateConnectionAsync(RemoteServiceName serviceName, CancellationToken cancellationToken)
{
var queue = _pools.GetOrAdd(serviceName, _ => new ConcurrentQueue<Connection>());
if (queue.TryDequeue(out var connection))
......@@ -51,7 +52,7 @@ public async Task<Connection> GetOrCreateConnectionAsync(string serviceName, Can
return new PooledConnection(this, serviceName, newConnection);
}
private void Free(string serviceName, Connection connection)
private void Free(RemoteServiceName serviceName, Connection connection)
{
using (_shutdownLock.DisposableRead())
{
......
......@@ -7,6 +7,7 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Remote;
namespace Microsoft.VisualStudio.LanguageServices.Remote
{
......@@ -17,10 +18,10 @@ private partial class ConnectionPool
private class PooledConnection : Connection
{
private readonly ConnectionPool _pool;
private readonly string _serviceName;
private readonly RemoteServiceName _serviceName;
private readonly Connection _connection;
public PooledConnection(ConnectionPool pool, string serviceName, Connection connection)
public PooledConnection(ConnectionPool pool, RemoteServiceName serviceName, Connection connection)
{
_pool = pool;
_serviceName = serviceName;
......
......@@ -14,8 +14,10 @@
using Microsoft.CodeAnalysis.ErrorReporting;
using Microsoft.CodeAnalysis.Execution;
using Microsoft.CodeAnalysis.Experiments;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Internal.Log;
using Microsoft.CodeAnalysis.Notification;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Remote;
using Microsoft.ServiceHub.Client;
using Microsoft.VisualStudio.Telemetry;
......@@ -34,15 +36,16 @@ internal sealed partial class ServiceHubRemoteHostClient : RemoteHostClient, IRe
private readonly ConnectionPool? _connectionPool;
private ServiceHubRemoteHostClient(
Workspace workspace,
HostWorkspaceServices services,
HubClient hubClient,
HostGroup hostGroup,
Stream stream)
: base(workspace)
: base(services)
{
if (workspace.Options.GetOption(RemoteHostOptions.EnableConnectionPool))
var optionService = services.GetRequiredService<IOptionService>();
if (optionService.GetOption(RemoteHostOptions.EnableConnectionPool))
{
int maxPoolConnection = workspace.Options.GetOption(RemoteHostOptions.MaxPoolConnection);
int maxPoolConnection = optionService.GetOption(RemoteHostOptions.MaxPoolConnection);
_connectionPool = new ConnectionPool(
connectionFactory: (serviceName, cancellationToken) => CreateConnectionAsync(serviceName, callbackTarget: null, cancellationToken),
......@@ -59,9 +62,9 @@ internal sealed partial class ServiceHubRemoteHostClient : RemoteHostClient, IRe
}
private void OnUnexpectedExceptionThrown(Exception unexpectedException)
=> RemoteHostCrashInfoBar.ShowInfoBar(Workspace, unexpectedException);
=> RemoteHostCrashInfoBar.ShowInfoBar(Services, unexpectedException);
public static async Task<RemoteHostClient?> CreateAsync(Workspace workspace, CancellationToken cancellationToken)
public static async Task<RemoteHostClient?> CreateAsync(HostWorkspaceServices services, CancellationToken cancellationToken)
{
using (Logger.LogBlock(FunctionId.ServiceHubRemoteHostClient_CreateAsync, cancellationToken))
{
......@@ -74,9 +77,9 @@ private void OnUnexpectedExceptionThrown(Exception unexpectedException)
// use the hub client logger for unexpected exceptions from devenv as well, so we have complete information in the log:
WatsonReporter.InitializeLogger(hubClient.Logger);
var remoteHostStream = await RequestServiceAsync(workspace, hubClient, WellKnownServiceHubServices.RemoteHostService, hostGroup, cancellationToken).ConfigureAwait(false);
var remoteHostStream = await RequestServiceAsync(services, hubClient, WellKnownServiceHubService.RemoteHost, hostGroup, cancellationToken).ConfigureAwait(false);
var client = new ServiceHubRemoteHostClient(workspace, hubClient, hostGroup, remoteHostStream);
var client = new ServiceHubRemoteHostClient(services, hubClient, hostGroup, remoteHostStream);
var uiCultureLCID = CultureInfo.CurrentUICulture.LCID;
var cultureLCID = CultureInfo.CurrentCulture.LCID;
......@@ -106,13 +109,15 @@ private void OnUnexpectedExceptionThrown(Exception unexpectedException)
}
public static async Task<Stream> RequestServiceAsync(
Workspace workspace,
HostWorkspaceServices services,
HubClient client,
string serviceName,
RemoteServiceName serviceName,
HostGroup hostGroup,
CancellationToken cancellationToken)
{
var descriptor = new ServiceDescriptor(serviceName) { HostGroup = hostGroup };
var is64bit = RemoteHostOptions.IsServiceHubProcess64Bit(services);
var descriptor = new ServiceDescriptor(serviceName.ToString(is64bit)) { HostGroup = hostGroup };
try
{
return await client.RequestServiceAsync(descriptor, cancellationToken).ConfigureAwait(false);
......@@ -127,7 +132,7 @@ private void OnUnexpectedExceptionThrown(Exception unexpectedException)
// we can assume that these exceptions indicate a failure and should be reported to the user.
cancellationToken.ThrowIfCancellationRequested();
RemoteHostCrashInfoBar.ShowInfoBar(workspace, e);
RemoteHostCrashInfoBar.ShowInfoBar(services, e);
// TODO: Propagate the original exception (see https://github.com/dotnet/roslyn/issues/40476)
throw new SoftCrashException("Unexpected exception from HubClient", e, cancellationToken);
......@@ -149,9 +154,8 @@ static bool ReportNonFatalWatson(Exception e, CancellationToken cancellationToke
public HostGroup HostGroup => _hostGroup;
public override string ClientId => _hostGroup.Id;
public override bool IsRemoteHost64Bit => RemoteHostOptions.IsServiceHubProcess64Bit(Workspace);
protected override Task<Connection?> TryCreateConnectionAsync(string serviceName, object? callbackTarget, CancellationToken cancellationToken)
protected override Task<Connection?> TryCreateConnectionAsync(RemoteServiceName serviceName, object? callbackTarget, CancellationToken cancellationToken)
{
// When callbackTarget is given, we can't share/pool connection since callbackTarget attaches a state to connection.
// so connection is only valid for that specific callbackTarget. it is up to the caller to keep connection open
......@@ -165,10 +169,10 @@ static bool ReportNonFatalWatson(Exception e, CancellationToken cancellationToke
return CreateConnectionAsync(serviceName, callbackTarget, cancellationToken).AsNullable();
}
private async Task<Connection> CreateConnectionAsync(string serviceName, object? callbackTarget, CancellationToken cancellationToken)
private async Task<Connection> CreateConnectionAsync(RemoteServiceName serviceName, object? callbackTarget, CancellationToken cancellationToken)
{
var serviceStream = await RequestServiceAsync(Workspace, _hubClient, serviceName, _hostGroup, cancellationToken).ConfigureAwait(false);
return new JsonRpcConnection(Workspace, _hubClient.Logger, callbackTarget, serviceStream);
var serviceStream = await RequestServiceAsync(Services, _hubClient, serviceName, _hostGroup, cancellationToken).ConfigureAwait(false);
return new JsonRpcConnection(Services, _hubClient.Logger, callbackTarget, serviceStream);
}
public override void Dispose()
......@@ -217,7 +221,7 @@ public Task<bool> IsExperimentEnabledAsync(string experimentName, CancellationTo
{
try
{
return Task.FromResult(Workspace.Services.GetRequiredService<IExperimentationService>().IsExperimentEnabled(experimentName));
return Task.FromResult(Services.GetRequiredService<IExperimentationService>().IsExperimentEnabled(experimentName));
}
catch (Exception ex) when (FatalError.ReportWithoutCrashUnlessCanceledAndPropagate(ex, cancellationToken))
{
......
......@@ -134,7 +134,7 @@ private async Task SynchronizePrimaryWorkspaceAsync(CancellationToken cancellati
var checksum = await solution.State.GetChecksumAsync(cancellationToken).ConfigureAwait(false);
_ = await client.TryRunRemoteAsync(
WellKnownServiceHubServices.RemoteHostService,
WellKnownServiceHubService.RemoteHost,
nameof(IRemoteHostService.SynchronizePrimaryWorkspaceAsync),
solution,
new object[] { checksum, solution.WorkspaceVersion },
......@@ -205,7 +205,7 @@ private void PushTextChanges(Document oldDocument, Document newDocument)
var state = await oldDocument.State.GetStateChecksumsAsync(CancellationToken).ConfigureAwait(false);
_ = await client.TryRunRemoteAsync(
WellKnownServiceHubServices.RemoteHostService,
WellKnownServiceHubService.RemoteHost,
nameof(IRemoteHostService.SynchronizeTextAsync),
solution: null,
new object[] { oldDocument.Id, state.Text, textChanges },
......
......@@ -106,7 +106,7 @@ private async Task StartWorkerAsync(CancellationToken cancellationToken)
// Pass ourselves in as the callback target for the OOP service. As it discovers
// todo comments it will call back into us to notify VS about it.
_keepAliveSession = await client.TryCreateKeepAliveSessionAsync(
WellKnownServiceHubServices.RemoteTodoCommentsService,
WellKnownServiceHubService.RemoteTodoCommentsService,
callbackTarget: this, cancellationToken).ConfigureAwait(false);
if (_keepAliveSession == null)
return;
......
......@@ -176,9 +176,10 @@ private static List<string> CollectServiceHubLogFilePaths()
// name our services more consistently to simplify filtering
// filter logs that are not relevant to Roslyn investigation
if (!name.Contains("-" + WellKnownServiceHubServices.NamePrefix) &&
if (!name.Contains("-" + RemoteServiceName.Prefix) &&
!name.Contains("-" + RemoteServiceName.IntelliCodeServiceName) &&
!name.Contains("-" + RemoteServiceName.RazorServiceName) &&
!name.Contains("-CodeLens") &&
!name.Contains("-pythia") &&
!name.Contains("-ManagedLanguage.IDE.RemoteHostClient") &&
!name.Contains("-hub"))
{
......
......@@ -7,6 +7,7 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Remote;
using Roslyn.Test.Utilities.Remote;
......@@ -22,9 +23,9 @@ public InProcRemoteHostClientFactory()
{
}
public Task<RemoteHostClient> CreateAsync(Workspace workspace, CancellationToken cancellationToken)
public Task<RemoteHostClient> CreateAsync(HostWorkspaceServices services, CancellationToken cancellationToken)
{
return InProcRemoteHostClient.CreateAsync(workspace, runCacheCleanup: false);
return InProcRemoteHostClient.CreateAsync(services, runCacheCleanup: false);
}
}
}
......@@ -102,7 +102,7 @@ public async Task TestSessionWithNoSolution()
var mock = new MockLogAndProgressService();
var client = await service.TryGetRemoteHostClientAsync(CancellationToken.None);
using var session = await client.TryCreateKeepAliveSessionAsync(WellKnownServiceHubServices.RemoteSymbolSearchUpdateEngine, callbackTarget: mock, CancellationToken.None);
using var session = await client.TryCreateKeepAliveSessionAsync(WellKnownServiceHubService.RemoteSymbolSearchUpdateEngine, callbackTarget: mock, CancellationToken.None);
await session.RunRemoteAsync(
nameof(IRemoteSymbolSearchUpdateEngine.UpdateContinuouslyAsync),
solution: null,
......@@ -120,17 +120,18 @@ public async Task TestSessionClosed()
service.Enable();
var client = (InProcRemoteHostClient)await service.TryGetRemoteHostClientAsync(CancellationToken.None);
var serviceName = new RemoteServiceName("Test");
// register local service
TestService testService = null;
client.RegisterService("Test", (s, p) =>
client.RegisterService(serviceName, (s, p) =>
{
testService = new TestService(s, p);
return testService;
});
// create session that stay alive until client alive (ex, SymbolSearchUpdateEngine)
using var session = await client.TryCreateKeepAliveSessionAsync("Test", callbackTarget: null, CancellationToken.None);
using var session = await client.TryCreateKeepAliveSessionAsync(serviceName, callbackTarget: null, CancellationToken.None);
// mimic unfortunate call that happens to be in the middle of communication.
var task = session.RunRemoteAsync("TestMethodAsync", solution: null, arguments: null, CancellationToken.None);
......
......@@ -5,11 +5,13 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Remote;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.VisualStudio.LanguageServer.Protocol;
......@@ -32,7 +34,7 @@ public async Task CSharpLanguageServiceTest()
{
var solution = workspace.CurrentSolution;
var results = await GetVsSearchResultsAsync(solution, WellKnownServiceHubServices.LanguageServer, "met");
var results = await GetVsSearchResultsAsync(solution, workspace.Services, "met");
Assert.Equal(1, results.Count);
Assert.Equal(1, results[0].Symbols.Length);
......@@ -57,7 +59,7 @@ public async Task CSharpLanguageServiceTest_MultipleResults()
{
var solution = workspace.CurrentSolution;
var results = await GetVsSearchResultsAsync(solution, WellKnownServiceHubServices.LanguageServer, "met");
var results = await GetVsSearchResultsAsync(solution, workspace.Services, "met");
Assert.Equal(1, results.Count);
Assert.Equal(4, results[0].Symbols.Length);
......@@ -77,7 +79,7 @@ End Sub
{
var solution = workspace.CurrentSolution;
var results = await GetVsSearchResultsAsync(solution, WellKnownServiceHubServices.LanguageServer, "met");
var results = await GetVsSearchResultsAsync(solution, workspace.Services, "met");
Assert.Equal(1, results.Count);
Assert.Equal(1, results[0].Symbols.Length);
......@@ -86,15 +88,15 @@ End Sub
}
}
private async Task<List<VSPublishSymbolParams>> GetVsSearchResultsAsync(Solution solution, string server, string query)
private async Task<List<VSPublishSymbolParams>> GetVsSearchResultsAsync(Solution solution, HostWorkspaceServices services, string query)
{
var client = (InProcRemoteHostClient)(await InProcRemoteHostClient.CreateAsync(solution.Workspace, runCacheCleanup: false));
var client = (InProcRemoteHostClient)await InProcRemoteHostClient.CreateAsync(services, runCacheCleanup: false);
var document = solution.Projects.First().Documents.First();
await UpdatePrimaryWorkspace(client, solution.WithDocumentFilePath(document.Id, @"c:\" + document.FilePath));
await UpdatePrimaryWorkspace(client, solution.WithDocumentFilePath(document.Id, Path.Combine(TempRoot.Root, document.FilePath)));
var callback = new Callback();
using (var jsonRpc = JsonRpc.Attach(await client.RequestServiceAsync(server), callback))
using (var jsonRpc = JsonRpc.Attach(await client.RequestServiceAsync(WellKnownServiceHubService.LanguageServer), callback))
{
var result = await jsonRpc.InvokeWithCancellationAsync<JObject>(
Methods.InitializeName,
......@@ -118,7 +120,7 @@ private async Task<List<VSPublishSymbolParams>> GetVsSearchResultsAsync(Solution
private async Task UpdatePrimaryWorkspace(InProcRemoteHostClient client, Solution solution)
{
Assert.True(await client.TryRunRemoteAsync(
WellKnownServiceHubServices.RemoteHostService,
WellKnownServiceHubService.RemoteHost,
nameof(IRemoteHostService.SynchronizePrimaryWorkspaceAsync),
solution,
new object[] { await solution.State.GetChecksumAsync(CancellationToken.None), _solutionVersion++ },
......
......@@ -75,7 +75,7 @@ public async Task TestRemoteHostSynchronize()
using (var workspace = TestWorkspace.CreateCSharp(code))
{
var client = (InProcRemoteHostClient)(await InProcRemoteHostClient.CreateAsync(workspace, runCacheCleanup: false));
var client = (InProcRemoteHostClient)await InProcRemoteHostClient.CreateAsync(workspace.Services, runCacheCleanup: false);
var solution = workspace.CurrentSolution;
......@@ -97,7 +97,7 @@ public async Task TestRemoteHostTextSynchronize()
using (var workspace = TestWorkspace.CreateCSharp(code))
{
var client = (InProcRemoteHostClient)(await InProcRemoteHostClient.CreateAsync(workspace, runCacheCleanup: false));
var client = (InProcRemoteHostClient)await InProcRemoteHostClient.CreateAsync(workspace.Services, runCacheCleanup: false);
var solution = workspace.CurrentSolution;
......@@ -115,7 +115,7 @@ public async Task TestRemoteHostTextSynchronize()
// sync
_ = await client.TryRunRemoteAsync(
WellKnownServiceHubServices.RemoteHostService,
WellKnownServiceHubService.RemoteHost,
nameof(IRemoteHostService.SynchronizeTextAsync),
solution: null,
new object[] { oldDocument.Id, oldState.Text, newText.GetTextChanges(oldText) },
......@@ -150,9 +150,9 @@ public async Task TestTodoComments()
var callback = new TodoCommentsListener();
using var client = await InProcRemoteHostClient.CreateAsync(workspace, runCacheCleanup: false);
using var client = await InProcRemoteHostClient.CreateAsync(workspace.Services, runCacheCleanup: false);
using var session = await client.TryCreateKeepAliveSessionAsync(
WellKnownServiceHubServices.RemoteTodoCommentsService,
WellKnownServiceHubService.RemoteTodoCommentsService,
callback,
cancellationTokenSource.Token);
......@@ -235,9 +235,9 @@ class Test { }");
var callback = new DesignerAttributeListener();
using var client = await InProcRemoteHostClient.CreateAsync(workspace, runCacheCleanup: false);
using var client = await InProcRemoteHostClient.CreateAsync(workspace.Services, runCacheCleanup: false);
using var session = await client.TryCreateKeepAliveSessionAsync(
WellKnownServiceHubServices.RemoteDesignerAttributeService,
WellKnownServiceHubService.RemoteDesignerAttributeService,
callback,
cancellationTokenSource.Token);
......@@ -281,7 +281,7 @@ public async Task TestUnknownProject()
var workspace = new AdhocWorkspace(TestHostServices.CreateHostServices());
var solution = workspace.CurrentSolution.AddProject("unknown", "unknown", NoCompilationConstants.LanguageName).Solution;
var client = (InProcRemoteHostClient)(await InProcRemoteHostClient.CreateAsync(workspace, runCacheCleanup: false));
var client = (InProcRemoteHostClient)await InProcRemoteHostClient.CreateAsync(workspace.Services, runCacheCleanup: false);
await UpdatePrimaryWorkspace(client, solution);
await VerifyAssetStorageAsync(client, solution);
......@@ -309,7 +309,7 @@ public async Task TestRemoteHostSynchronizeIncrementalUpdate()
{
using var workspace = new TestWorkspace();
var client = (InProcRemoteHostClient)await InProcRemoteHostClient.CreateAsync(workspace, runCacheCleanup: false);
var client = (InProcRemoteHostClient)await InProcRemoteHostClient.CreateAsync(workspace.Services, runCacheCleanup: false);
var solution = Populate(workspace.CurrentSolution);
......@@ -481,7 +481,7 @@ private static (Project, Document) GetProjectAndDocument(Solution solution, stri
private async Task UpdatePrimaryWorkspace(InProcRemoteHostClient client, Solution solution)
{
Assert.True(await client.TryRunRemoteAsync(
WellKnownServiceHubServices.RemoteHostService,
WellKnownServiceHubService.RemoteHost,
nameof(IRemoteHostService.SynchronizePrimaryWorkspaceAsync),
solution,
new object[] { await solution.State.GetChecksumAsync(CancellationToken.None), _solutionVersion++ },
......
......@@ -19,12 +19,12 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
internal sealed class RazorLanguageServiceClient
{
private readonly RemoteHostClient _client;
private readonly string _serviceName;
private readonly RemoteServiceName _serviceName;
internal RazorLanguageServiceClient(RemoteHostClient client, string serviceName)
{
_client = client;
_serviceName = serviceName;
_serviceName = new RemoteServiceName(serviceName);
}
public Task<Optional<T>> TryRunRemoteAsync<T>(string targetName, Solution? solution, IReadOnlyList<object?> arguments, object? callbackTarget, CancellationToken cancellationToken)
......
......@@ -70,7 +70,7 @@ public static void SetLoggers(IGlobalOptionService optionService, IThreadingCont
var functionIds = GetFunctionIds(options).ToList();
threadingContext.JoinableTaskFactory.Run(() => client.TryRunRemoteAsync(
WellKnownServiceHubServices.RemoteHostService,
WellKnownServiceHubService.RemoteHost,
nameof(IRemoteHostService.SetLoggingFunctionIds),
solution: null,
new object[] { loggerTypes, functionIds },
......
......@@ -64,7 +64,7 @@ private async Task<bool> TryAddSemanticClassificationsInRemoteProcessAsync(Docum
return false;
var classifiedSpans = await client.TryRunRemoteAsync<SerializableClassifiedSpans>(
WellKnownServiceHubServices.CodeAnalysisService,
WellKnownServiceHubService.CodeAnalysis,
nameof(IRemoteSemanticClassificationService.GetSemanticClassificationsAsync),
document.Project.Solution,
new object[] { document.Id, textSpan },
......
......@@ -2,16 +2,25 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#nullable enable
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Remote;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.ExternalAccess.Pythia.Api
{
internal static class PythiaRemoteHostClient
{
public static async Task<Optional<T>> TryRunRemoteAsync<T>(Workspace workspace, string serviceName, string targetName, Solution solution, IReadOnlyList<object> arguments, CancellationToken cancellationToken)
public static Task<Optional<T>> TryRunRemoteAsync<T>(Workspace workspace, string serviceName, string targetName, Solution? solution, IReadOnlyList<object?> arguments, CancellationToken cancellationToken)
{
Contract.ThrowIfFalse(serviceName == "pythia");
return TryRunRemoteAsync<T>(workspace, targetName, solution, arguments, cancellationToken);
}
public static async Task<Optional<T>> TryRunRemoteAsync<T>(Workspace workspace, string targetName, Solution? solution, IReadOnlyList<object?> arguments, CancellationToken cancellationToken)
{
var client = await RemoteHostClient.TryGetClientAsync(workspace, cancellationToken).ConfigureAwait(false);
if (client == null)
......@@ -19,12 +28,7 @@ public static async Task<Optional<T>> TryRunRemoteAsync<T>(Workspace workspace,
return default;
}
if (client.IsRemoteHost64Bit)
{
serviceName += "64";
}
return await client.TryRunRemoteAsync<T>(serviceName, targetName, solution, arguments, callbackTarget: null, cancellationToken).ConfigureAwait(false);
return await client.TryRunRemoteAsync<T>(WellKnownServiceHubService.IntelliCode, targetName, solution, arguments, callbackTarget: null, cancellationToken).ConfigureAwait(false);
}
}
}
......@@ -21,13 +21,13 @@ internal UnitTestingRemoteHostClientWrapper(RemoteHostClient underlyingObject)
public async Task<UnitTestingKeepAliveSessionWrapper> TryCreateUnitTestingKeepAliveSessionWrapperAsync(string serviceName, CancellationToken cancellationToken)
{
var keepAliveSession = await UnderlyingObject.TryCreateKeepAliveSessionAsync(serviceName, callbackTarget: null, cancellationToken).ConfigureAwait(false);
var keepAliveSession = await UnderlyingObject.TryCreateKeepAliveSessionAsync(new RemoteServiceName(serviceName), callbackTarget: null, cancellationToken).ConfigureAwait(false);
return new UnitTestingKeepAliveSessionWrapper(keepAliveSession);
}
public async Task<UnitTestingSessionWithSolutionWrapper> TryCreateUnitingSessionWithSolutionWrapperAsync(string serviceName, Solution solution, CancellationToken cancellationToken)
{
var keepAliveSession = await UnderlyingObject.TryCreateKeepAliveSessionAsync(serviceName, callbackTarget: null, cancellationToken).ConfigureAwait(false);
var keepAliveSession = await UnderlyingObject.TryCreateKeepAliveSessionAsync(new RemoteServiceName(serviceName), callbackTarget: null, cancellationToken).ConfigureAwait(false);
if (keepAliveSession == null)
{
return default;
......
......@@ -42,7 +42,7 @@ internal static partial class DeclarationFinder
var solution = project.Solution;
var result = await client.TryRunRemoteAsync<IList<SerializableSymbolAndProjectId>>(
WellKnownServiceHubServices.CodeAnalysisService,
WellKnownServiceHubService.CodeAnalysis,
nameof(IRemoteSymbolFinder.FindAllDeclarationsWithNormalQueryAsync),
solution,
new object[] { project.Id, query.Name, query.Kind, criteria },
......
......@@ -45,7 +45,7 @@ internal static partial class DeclarationFinder
if (client != null)
{
var result = await client.TryRunRemoteAsync<IList<SerializableSymbolAndProjectId>>(
WellKnownServiceHubServices.CodeAnalysisService,
WellKnownServiceHubService.CodeAnalysis,
nameof(IRemoteSymbolFinder.FindSolutionSourceDeclarationsWithNormalQueryAsync),
solution,
new object[] { name, ignoreCase, criteria },
......@@ -84,7 +84,7 @@ internal static partial class DeclarationFinder
if (client != null)
{
var result = await client.TryRunRemoteAsync<IList<SerializableSymbolAndProjectId>>(
WellKnownServiceHubServices.CodeAnalysisService,
WellKnownServiceHubService.CodeAnalysis,
nameof(IRemoteSymbolFinder.FindProjectSourceDeclarationsWithNormalQueryAsync),
project.Solution,
new object[] { project.Id, name, ignoreCase, criteria },
......@@ -119,7 +119,7 @@ internal static partial class DeclarationFinder
if (client != null)
{
var result = await client.TryRunRemoteAsync<IList<SerializableSymbolAndProjectId>>(
WellKnownServiceHubServices.CodeAnalysisService,
WellKnownServiceHubService.CodeAnalysis,
nameof(IRemoteSymbolFinder.FindSolutionSourceDeclarationsWithPatternAsync),
solution,
new object[] { pattern, criteria },
......@@ -153,7 +153,7 @@ internal static partial class DeclarationFinder
if (client != null)
{
var result = await client.TryRunRemoteAsync<IList<SerializableSymbolAndProjectId>>(
WellKnownServiceHubServices.CodeAnalysisService,
WellKnownServiceHubService.CodeAnalysis,
nameof(IRemoteSymbolFinder.FindProjectSourceDeclarationsWithPatternAsync),
project.Solution,
new object[] { project.Id, pattern, criteria },
......
......@@ -34,7 +34,7 @@ internal static partial class DependentTypeFinder
if (client != null)
{
var result = await client.TryRunRemoteAsync<ImmutableArray<SerializableSymbolAndProjectId>>(
WellKnownServiceHubServices.CodeAnalysisService,
WellKnownServiceHubService.CodeAnalysis,
remoteFunctionName,
solution,
new object?[]
......
......@@ -30,7 +30,7 @@ public static partial class SymbolFinder
var serverCallback = new FindLiteralsServerCallback(solution, progress);
var success = await client.TryRunRemoteAsync(
WellKnownServiceHubServices.CodeAnalysisService,
WellKnownServiceHubService.CodeAnalysis,
nameof(IRemoteSymbolFinder.FindLiteralReferencesAsync),
solution,
new object[] { value, typeCode },
......
......@@ -40,7 +40,7 @@ public static partial class SymbolFinder
var serverCallback = new FindReferencesServerCallback(solution, progress, cancellationToken);
var success = await client.TryRunRemoteAsync(
WellKnownServiceHubServices.CodeAnalysisService,
WellKnownServiceHubService.CodeAnalysis,
nameof(IRemoteSymbolFinder.FindReferencesAsync),
solution,
new object[]
......
......@@ -4,6 +4,7 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Host;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Remote
......@@ -12,21 +13,21 @@ internal partial class DefaultRemoteHostClientServiceFactory
{
public class RemoteHostClientService : IRemoteHostClientService
{
private readonly Workspace _workspace;
private readonly HostWorkspaceServices _services;
private readonly IRemoteHostClientFactory _remoteHostClientFactory;
private AsyncLazy<RemoteHostClient> _lazyInstance;
public RemoteHostClientService(Workspace workspace)
public RemoteHostClientService(HostWorkspaceServices services)
{
var remoteHostClientFactory = workspace.Services.GetService<IRemoteHostClientFactory>();
var remoteHostClientFactory = services.GetService<IRemoteHostClientFactory>();
if (remoteHostClientFactory == null)
{
// no implementation of remote host client
return;
}
_workspace = workspace;
_services = services;
_remoteHostClientFactory = remoteHostClientFactory;
_lazyInstance = CreateNewLazyRemoteHostClient();
......@@ -59,7 +60,7 @@ public async Task RequestNewRemoteHostAsync(CancellationToken cancellationToken)
}
private AsyncLazy<RemoteHostClient> CreateNewLazyRemoteHostClient()
=> new AsyncLazy<RemoteHostClient>(c => _remoteHostClientFactory.CreateAsync(_workspace, c), cacheResult: true);
=> new AsyncLazy<RemoteHostClient>(c => _remoteHostClientFactory.CreateAsync(_services, c), cacheResult: true);
}
}
}
......@@ -22,6 +22,6 @@ public DefaultRemoteHostClientServiceFactory()
}
public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices)
=> new RemoteHostClientService(workspaceServices.Workspace);
=> new RemoteHostClientService(workspaceServices);
}
}
......@@ -17,6 +17,6 @@ namespace Microsoft.CodeAnalysis.Remote
/// </summary>
internal interface IRemoteHostClientFactory : IWorkspaceService
{
Task<RemoteHostClient?> CreateAsync(Workspace workspace, CancellationToken cancellationToken);
Task<RemoteHostClient?> CreateAsync(HostWorkspaceServices services, CancellationToken cancellationToken);
}
}
......@@ -15,23 +15,6 @@ internal interface IRemoteHostClientService : IWorkspaceService
{
bool IsEnabled();
/// <summary>
/// Request new remote host.
///
/// this is designed to be not disruptive to existing callers and to support scenarios where
/// features required to reload user extension dlls without re-launching VS.
///
/// if someone requests new remote host, all new callers for <see cref="TryGetRemoteHostClientAsync(CancellationToken)"/> will
/// receive a new remote host client that connects to a new remote host.
///
/// existing remoteHostClient will still remain connected to old host and that old host will eventually go away once all existing clients
/// are done with their requests.
///
/// callers can subscribe to <see cref="RemoteHostClient.StatusChanged"/> event to see whether client is going away if
/// caller is designed to hold onto a service for a while to react to remote host change.
/// </summary>
Task RequestNewRemoteHostAsync(CancellationToken cancellationToken);
/// <summary>
/// Get <see cref="RemoteHostClient"/> to current RemoteHost
/// </summary>
......
......@@ -27,15 +27,15 @@ namespace Microsoft.CodeAnalysis.Remote
/// </summary>
internal abstract class RemoteHostClient : IDisposable
{
public readonly Workspace Workspace;
public readonly HostWorkspaceServices Services;
public event EventHandler<bool>? StatusChanged;
internal readonly IRemotableDataService RemotableDataService;
protected RemoteHostClient(Workspace workspace)
protected RemoteHostClient(HostWorkspaceServices services)
{
Workspace = workspace;
RemotableDataService = workspace.Services.GetRequiredService<IRemotableDataService>();
Services = services;
RemotableDataService = services.GetRequiredService<IRemotableDataService>();
}
/// <summary>
......@@ -53,9 +53,7 @@ protected RemoteHostClient(Workspace workspace)
/// Creating session could fail if remote host is not available. one of example will be user killing
/// remote host.
/// </summary>
protected abstract Task<Connection?> TryCreateConnectionAsync(string serviceName, object? callbackTarget, CancellationToken cancellationToken);
public abstract bool IsRemoteHost64Bit { get; }
protected abstract Task<Connection?> TryCreateConnectionAsync(RemoteServiceName serviceName, object? callbackTarget, CancellationToken cancellationToken);
protected void Started()
{
......@@ -98,7 +96,7 @@ public static string CreateClientId(string prefix)
/// <summary>
/// Creates <see cref="KeepAliveSession"/> for the <paramref name="serviceName"/>, otherwise returns <see langword="null"/>.
/// </summary>
public async Task<KeepAliveSession?> TryCreateKeepAliveSessionAsync(string serviceName, object? callbackTarget, CancellationToken cancellationToken)
public async Task<KeepAliveSession?> TryCreateKeepAliveSessionAsync(RemoteServiceName serviceName, object? callbackTarget, CancellationToken cancellationToken)
{
var connection = await TryCreateConnectionAsync(serviceName, callbackTarget, cancellationToken).ConfigureAwait(false);
if (connection == null)
......@@ -109,7 +107,7 @@ public static string CreateClientId(string prefix)
return new KeepAliveSession(connection, RemotableDataService);
}
public async Task<bool> TryRunRemoteAsync(string serviceName, string targetName, Solution? solution, IReadOnlyList<object?> arguments, object? callbackTarget, CancellationToken cancellationToken)
public async Task<bool> TryRunRemoteAsync(RemoteServiceName serviceName, string targetName, Solution? solution, IReadOnlyList<object?> arguments, object? callbackTarget, CancellationToken cancellationToken)
{
using var connection = await TryCreateConnectionAsync(serviceName, callbackTarget, cancellationToken).ConfigureAwait(false);
if (connection == null)
......@@ -121,10 +119,10 @@ public async Task<bool> TryRunRemoteAsync(string serviceName, string targetName,
return true;
}
public Task<Optional<T>> TryRunRemoteAsync<T>(string serviceName, string targetName, Solution? solution, IReadOnlyList<object?> arguments, object? callbackTarget, CancellationToken cancellationToken)
public Task<Optional<T>> TryRunRemoteAsync<T>(RemoteServiceName serviceName, string targetName, Solution? solution, IReadOnlyList<object?> arguments, object? callbackTarget, CancellationToken cancellationToken)
=> TryRunRemoteAsync<T>(serviceName, targetName, solution, arguments, callbackTarget, dataReader: null, cancellationToken);
public async Task<Optional<T>> TryRunRemoteAsync<T>(string serviceName, string targetName, Solution? solution, IReadOnlyList<object?> arguments, object? callbackTarget, Func<Stream, CancellationToken, Task<T>>? dataReader, CancellationToken cancellationToken)
public async Task<Optional<T>> TryRunRemoteAsync<T>(RemoteServiceName serviceName, string targetName, Solution? solution, IReadOnlyList<object?> arguments, object? callbackTarget, Func<Stream, CancellationToken, Task<T>>? dataReader, CancellationToken cancellationToken)
{
using var connection = await TryCreateConnectionAsync(serviceName, callbackTarget, cancellationToken).ConfigureAwait(false);
if (connection == null)
......@@ -188,15 +186,14 @@ internal static async Task<T> RunRemoteAsync<T>(Connection connection, IRemotabl
/// </summary>
public class NoOpClient : RemoteHostClient
{
public NoOpClient(Workspace workspace)
: base(workspace)
public NoOpClient(HostWorkspaceServices services)
: base(services)
{
}
public override string ClientId => nameof(NoOpClient);
public override bool IsRemoteHost64Bit => false;
protected override Task<Connection?> TryCreateConnectionAsync(string serviceName, object? callbackTarget, CancellationToken cancellationToken)
protected override Task<Connection?> TryCreateConnectionAsync(RemoteServiceName serviceName, object? callbackTarget, CancellationToken cancellationToken)
=> SpecializedTasks.Null<Connection>();
}
......
// 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 file in the project root for more information.
#nullable enable
using System;
using System.Diagnostics.CodeAnalysis;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Remote
{
/// <summary>
/// Abstract the name of a remote service.
/// </summary>
/// <remarks>
/// Allows partner teams to specify bitness-specific service name, while we can use bitness agnostic id for well-known services.
/// TODO: Update LUT and SBD to use well-known ids and remove this abstraction (https://github.com/dotnet/roslyn/issues/44327).
/// </remarks>
internal readonly struct RemoteServiceName : IEquatable<RemoteServiceName>
{
internal const string Prefix = "roslyn";
internal const string IntelliCodeServiceName = "pythia";
internal const string RazorServiceName = "razorLanguageService";
public readonly WellKnownServiceHubService WellKnownService;
public readonly string? CustomServiceName;
public RemoteServiceName(WellKnownServiceHubService wellKnownService)
{
WellKnownService = wellKnownService;
CustomServiceName = null;
}
/// <summary>
/// Exact service name - must be reflect the bitness of the ServiceHub process.
/// </summary>
public RemoteServiceName(string customServiceName)
{
WellKnownService = WellKnownServiceHubService.None;
CustomServiceName = customServiceName;
}
public string ToString(bool isRemoteHost64Bit)
{
const string Suffix64 = "64";
return CustomServiceName ?? (WellKnownService, isRemoteHost64Bit) switch
{
(WellKnownServiceHubService.RemoteHost, false) => Prefix + nameof(WellKnownServiceHubService.RemoteHost),
(WellKnownServiceHubService.RemoteHost, true) => Prefix + nameof(WellKnownServiceHubService.RemoteHost) + Suffix64,
(WellKnownServiceHubService.CodeAnalysis, false) => Prefix + nameof(WellKnownServiceHubService.CodeAnalysis),
(WellKnownServiceHubService.CodeAnalysis, true) => Prefix + nameof(WellKnownServiceHubService.CodeAnalysis) + Suffix64,
(WellKnownServiceHubService.RemoteSymbolSearchUpdateEngine, false) => Prefix + nameof(WellKnownServiceHubService.RemoteSymbolSearchUpdateEngine),
(WellKnownServiceHubService.RemoteSymbolSearchUpdateEngine, true) => Prefix + nameof(WellKnownServiceHubService.RemoteSymbolSearchUpdateEngine) + Suffix64,
(WellKnownServiceHubService.RemoteDesignerAttributeService, false) => Prefix + nameof(WellKnownServiceHubService.RemoteDesignerAttributeService),
(WellKnownServiceHubService.RemoteDesignerAttributeService, true) => Prefix + nameof(WellKnownServiceHubService.RemoteDesignerAttributeService) + Suffix64,
(WellKnownServiceHubService.RemoteProjectTelemetryService, false) => Prefix + nameof(WellKnownServiceHubService.RemoteProjectTelemetryService),
(WellKnownServiceHubService.RemoteProjectTelemetryService, true) => Prefix + nameof(WellKnownServiceHubService.RemoteProjectTelemetryService) + Suffix64,
(WellKnownServiceHubService.RemoteTodoCommentsService, false) => Prefix + nameof(WellKnownServiceHubService.RemoteTodoCommentsService),
(WellKnownServiceHubService.RemoteTodoCommentsService, true) => Prefix + nameof(WellKnownServiceHubService.RemoteTodoCommentsService) + Suffix64,
(WellKnownServiceHubService.LanguageServer, false) => Prefix + nameof(WellKnownServiceHubService.LanguageServer),
(WellKnownServiceHubService.LanguageServer, true) => Prefix + nameof(WellKnownServiceHubService.LanguageServer) + Suffix64,
(WellKnownServiceHubService.IntelliCode, false) => IntelliCodeServiceName,
(WellKnownServiceHubService.IntelliCode, true) => IntelliCodeServiceName + Suffix64,
(WellKnownServiceHubService.Razor, false) => RazorServiceName,
(WellKnownServiceHubService.Razor, true) => RazorServiceName + Suffix64,
_ => throw ExceptionUtilities.UnexpectedValue(WellKnownService),
};
}
public override bool Equals(object? obj)
=> obj is RemoteServiceName name && Equals(name);
public override int GetHashCode()
=> Hash.Combine(CustomServiceName, (int)WellKnownService);
public bool Equals([AllowNull] RemoteServiceName other)
=> CustomServiceName == other.CustomServiceName && WellKnownService == other.WellKnownService;
public static bool operator ==(RemoteServiceName left, RemoteServiceName right)
=> left.Equals(right);
public static bool operator !=(RemoteServiceName left, RemoteServiceName right)
=> !(left == right);
public static implicit operator RemoteServiceName(WellKnownServiceHubService wellKnownService)
=> new RemoteServiceName(wellKnownService);
}
}
......@@ -32,7 +32,7 @@ public static async Task<SessionWithSolution> CreateAsync(KeepAliveSession keepA
// set connection state for this session.
// we might remove this in future. see https://github.com/dotnet/roslyn/issues/24836
await keepAliveSession.RunRemoteAsync(
WellKnownServiceHubServices.ServiceHubServiceBase_Initialize,
"Initialize",
solution: null,
new object[] { scope.SolutionInfo },
cancellationToken).ConfigureAwait(false);
......
// 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 file in the project root for more information.
#nullable enable
namespace Microsoft.CodeAnalysis.Remote
{
internal enum WellKnownServiceHubService
{
None,
RemoteHost,
CodeAnalysis,
RemoteSymbolSearchUpdateEngine,
RemoteDesignerAttributeService,
RemoteProjectTelemetryService,
RemoteTodoCommentsService,
LanguageServer,
IntelliCode,
Razor
}
}
// 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 file in the project root for more information.
namespace Microsoft.CodeAnalysis.Remote
{
internal static class WellKnownServiceHubServices
{
public const string NamePrefix = "roslyn";
public static void Set64bit(bool x64)
{
var bit = x64 ? "64" : "";
RemoteHostService = "roslynRemoteHost" + bit;
CodeAnalysisService = NamePrefix + "CodeAnalysis" + bit;
RemoteDesignerAttributeService = NamePrefix + "RemoteDesignerAttributeService" + bit;
RemoteProjectTelemetryService = NamePrefix + "RemoteProjectTelemetryService" + bit;
RemoteSymbolSearchUpdateEngine = NamePrefix + "RemoteSymbolSearchUpdateEngine" + bit;
RemoteTodoCommentsService = NamePrefix + "RemoteTodoCommentsService" + bit;
LanguageServer = NamePrefix + "LanguageServer" + bit;
}
public static string RemoteHostService { get; private set; } = NamePrefix + "RemoteHost";
public static string CodeAnalysisService { get; private set; } = NamePrefix + "CodeAnalysis";
public static string RemoteSymbolSearchUpdateEngine { get; private set; } = NamePrefix + "RemoteSymbolSearchUpdateEngine";
public static string RemoteDesignerAttributeService { get; private set; } = NamePrefix + "RemoteDesignerAttributeService";
public static string RemoteProjectTelemetryService { get; private set; } = NamePrefix + "RemoteProjectTelemetryService";
public static string RemoteTodoCommentsService { get; private set; } = NamePrefix + "RemoteTodoCommentsService";
public static string LanguageServer { get; private set; } = NamePrefix + "LanguageServer";
// these are OOP implementation itself should care. not features that consume OOP care
public const string ServiceHubServiceBase_Initialize = "Initialize";
}
}
......@@ -53,7 +53,7 @@ internal static partial class ConflictResolver
if (client != null)
{
var result = await client.TryRunRemoteAsync<SerializableConflictResolution?>(
WellKnownServiceHubServices.CodeAnalysisService,
WellKnownServiceHubService.CodeAnalysis,
nameof(IRemoteRenamer.ResolveConflictsAsync),
solution,
new object?[]
......
......@@ -137,7 +137,7 @@ internal sealed partial class RenameLocations
if (client != null)
{
var result = await client.TryRunRemoteAsync<SerializableRenameLocations?>(
WellKnownServiceHubServices.CodeAnalysisService,
WellKnownServiceHubService.CodeAnalysis,
nameof(IRemoteRenamer.FindRenameLocationsAsync),
solution,
new object[]
......
......@@ -129,7 +129,7 @@ internal static Task<RenameLocations> FindRenameLocationsAsync(Solution solution
if (client != null)
{
var result = await client.TryRunRemoteAsync<SerializableConflictResolution?>(
WellKnownServiceHubServices.CodeAnalysisService,
WellKnownServiceHubService.CodeAnalysis,
nameof(IRemoteRenamer.RenameSymbolAsync),
solution,
new object?[]
......
......@@ -26,7 +26,7 @@ protected ServiceHubServiceBase(IServiceProvider serviceProvider, Stream stream,
}
/// <summary>
/// Invoked remotely - <see cref="WellKnownServiceHubServices.ServiceHubServiceBase_Initialize"/>
/// Invoked remotely.
/// </summary>
[Obsolete]
public virtual void Initialize(PinnedSolutionInfo info)
......
......@@ -373,7 +373,7 @@ internal enum FunctionId
// obsolete: RemoteHostClientService_AddGlobalAssetsAsync = 299,
// obsolete: RemoteHostClientService_RemoveGlobalAssets = 300,
RemoteHostClientService_Enabled = 301,
RemoteHostClientService_Restarted = 302,
// obsolete: RemoteHostClientService_Restarted = 302,
RemoteHostService_SynchronizePrimaryWorkspaceAsync = 303,
// obsolete: RemoteHostService_SynchronizeGlobalAssetsAsync = 304,
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册