diff --git a/src/Workspaces/CoreTestUtilities/Remote/InProcRemostHostClient.cs b/src/Workspaces/CoreTestUtilities/Remote/InProcRemostHostClient.cs index 9b1d77c30520c..ec7178c14b53c 100644 --- a/src/Workspaces/CoreTestUtilities/Remote/InProcRemostHostClient.cs +++ b/src/Workspaces/CoreTestUtilities/Remote/InProcRemostHostClient.cs @@ -9,22 +9,20 @@ using System.Diagnostics; using System.IO; using System.IO.Pipelines; +using System.Runtime.CompilerServices; +using System.Runtime.ExceptionServices; using System.Runtime.Remoting; -using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Experiments; using Microsoft.CodeAnalysis.Extensions; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Serialization; -using Microsoft.CodeAnalysis.TodoComments; using Microsoft.ServiceHub.Framework; using Microsoft.VisualStudio.Threading; using Nerdbank.Streams; -using Roslyn.Test.Utilities; using Roslyn.Utilities; using StreamJsonRpc; -using Xunit.Abstractions; namespace Microsoft.CodeAnalysis.Remote.Testing { @@ -41,7 +39,7 @@ public static async Task CreateAsync(HostWorkspaceServices ser var remoteHostStream = await inprocServices.RequestServiceAsync(WellKnownServiceHubService.RemoteHost).ConfigureAwait(false); - var instance = new InProcRemoteHostClient(services, inprocServices, remoteHostStream); + var instance = new InProcRemoteHostClient(services, inprocServices, traceListener, remoteHostStream); // make sure connection is done right var uiCultureLCIDE = 0; @@ -61,10 +59,19 @@ await instance._endPoint.InvokeAsync( private InProcRemoteHostClient( HostWorkspaceServices services, InProcRemoteServices inprocServices, + TraceListener? traceListener, Stream stream) { _workspaceServices = services; - _logger = new TraceSource("Default"); + _logger = new TraceSource("Default") + { + Switch = { Level = SourceLevels.Information }, + }; + + if (traceListener != null) + { + _logger.Listeners.Add(traceListener); + } _inprocServices = inprocServices; @@ -147,7 +154,10 @@ public override void Dispose() } private void OnDisconnected(JsonRpcDisconnectedEventArgs e) - => Dispose(); + { + _logger?.TraceInformation($"Closing InProcRemoteHostClient ({e.Reason}) Exception: {e.Exception})"); + Dispose(); + } public sealed class ServiceProvider : IServiceProvider { @@ -193,7 +203,21 @@ public event EventHandler? AvailabilityChanged public ValueTask GetProxyAsync(ServiceRpcDescriptor descriptor, ServiceActivationOptions options, CancellationToken cancellationToken) where T : class { - var pipePair = FullDuplexStream.CreatePipePair(); + var fullDuplexStreams = FullDuplexStream.CreatePair(); + + if (descriptor.Moniker == SolutionAssetProvider.ServiceDescriptor.Moniker) + { + //MonitorForDisposal(ref fullDuplexStreams.Item1, descriptor, 1, _services.ServiceProvider.TraceSource); + //MonitorForDisposal(ref fullDuplexStreams.Item2, descriptor, 2, _services.ServiceProvider.TraceSource); + } + + var pipePair = (fullDuplexStreams.Item1.UsePipe(), fullDuplexStreams.Item2.UsePipe()); + + if (((ServiceJsonRpcDescriptor)descriptor).MultiplexingStreamOptions is { } multiplexingStreamOptions && _services.ServiceProvider.TraceSource is { } traceSource) + { + descriptor = (ServiceDescriptor)((ServiceDescriptor)descriptor).WithMultiplexingStream( + new MultiplexingStream.Options(multiplexingStreamOptions) { TraceSource = traceSource }); + } var clientConnection = descriptor .WithTraceSource(_services.ServiceProvider.TraceSource) @@ -214,11 +238,26 @@ public event EventHandler? AvailabilityChanged // Creates service instance and connects it to the pipe. // We don't need to store the instance anywhere. - _ = _services.CreateBrokeredService(descriptor, pipePair.Item1, options); + _ = _services.CreateBrokeredService((ServiceDescriptor)descriptor, pipePair.Item1, options); clientConnection.StartListening(); return new ValueTask(clientConnection.ConstructRpcClient()); + + //static void MonitorForDisposal(ref Stream stream, ServiceRpcDescriptor descriptor, int item, TraceSource traceSource) + //{ + // if (traceSource is null) + // return; + + // var wrappedStream = new MonitoringStream(stream); + // wrappedStream.Disposed += + // delegate + // { + // traceSource?.TraceInformation($"Disposing pipe {item} for '{descriptor.Moniker}': {new StackTrace()}"); + // //Debugger.Launch(); + // }; + // stream = wrappedStream; + //} } } @@ -236,7 +275,7 @@ public InProcRemoteServices(HostWorkspaceServices workspaceServices, TraceListen { var remoteLogger = new TraceSource("InProcRemoteClient") { - Switch = { Level = SourceLevels.Verbose }, + Switch = { Level = SourceLevels.Information }, }; if (traceListener != null) @@ -298,7 +337,7 @@ public void RegisterRemoteBrokeredService(BrokeredServiceBase.IFactory serviceFa _remoteBrokeredServicesMap.Add(moniker, serviceFactory); } - public object CreateBrokeredService(ServiceRpcDescriptor descriptor, IDuplexPipe pipe, ServiceActivationOptions options) + public object CreateBrokeredService(ServiceJsonRpcDescriptor descriptor, IDuplexPipe pipe, ServiceActivationOptions options) { if (_inProcBrokeredServicesMap.TryGetValue(descriptor.Moniker, out var inProcFactory)) { @@ -306,6 +345,12 @@ public object CreateBrokeredService(ServiceRpcDescriptor descriptor, IDuplexPipe // Currently don't support callback creation as we don't have in-proc service with callbacks yet. Contract.ThrowIfFalse(descriptor.ClientInterface == null); + if (descriptor.MultiplexingStreamOptions is not null && ServiceProvider.TraceSource is { } traceSource) + { + descriptor = ((ServiceDescriptor)descriptor).WithMultiplexingStream( + new MultiplexingStream.Options(descriptor.MultiplexingStreamOptions) { TraceSource = traceSource }); + } + var serviceConnection = descriptor.WithTraceSource(ServiceProvider.TraceSource).ConstructRpcConnection(pipe); var service = inProcFactory(); diff --git a/src/Workspaces/Remote/Core/ServiceDescriptor.cs b/src/Workspaces/Remote/Core/ServiceDescriptor.cs index ef53f79207872..765dc0ab17e4c 100644 --- a/src/Workspaces/Remote/Core/ServiceDescriptor.cs +++ b/src/Workspaces/Remote/Core/ServiceDescriptor.cs @@ -5,6 +5,7 @@ #nullable enable using System; +using System.Diagnostics; using System.IO.Pipelines; using System.Reflection; using MessagePack; @@ -73,6 +74,16 @@ private static MessagePackFormatter ConfigureFormatter(MessagePackFormatter form protected override JsonRpcConnection CreateConnection(JsonRpc jsonRpc) { + //if (Moniker == SolutionAssetProvider.ServiceDescriptor.Moniker) + //{ + // jsonRpc.Disconnected += + // delegate + // { + // jsonRpc.TraceSource?.TraceInformation($"Disconnected '{Moniker}': {new StackTrace()}"); + // //Debugger.Launch(); + // }; + //} + jsonRpc.CancelLocallyInvokedMethodsWhenConnectionIsClosed = true; var connection = base.CreateConnection(jsonRpc); connection.LocalRpcTargetOptions = s_jsonRpcTargetOptions; @@ -99,6 +110,19 @@ public override ServiceRpcDescriptor WithMultiplexingStream(MultiplexingStream? return result; } + public new ServiceDescriptor WithMultiplexingStream(MultiplexingStream.Options multiplexingStreamOptions) + { + var baseResult = base.WithMultiplexingStream(multiplexingStreamOptions); + if (baseResult is ServiceDescriptor serviceDescriptor) + return serviceDescriptor; + + // Work around incorrect implementation in 16.8 Preview 2 + var result = (ServiceDescriptor)WithMultiplexingStream((MultiplexingStream?)null); + result = (ServiceDescriptor)result.Clone(); + typeof(ServiceJsonRpcDescriptor).GetProperty(nameof(MultiplexingStreamOptions))!.SetValue(result, value: multiplexingStreamOptions?.GetFrozenCopy()); + return result; + } + internal static class TestAccessor { public static MessagePackSerializerOptions Options => s_options; diff --git a/src/Workspaces/Remote/Core/ServiceDescriptors.cs b/src/Workspaces/Remote/Core/ServiceDescriptors.cs index 67e83c31d1cf8..cb452f5c22545 100644 --- a/src/Workspaces/Remote/Core/ServiceDescriptors.cs +++ b/src/Workspaces/Remote/Core/ServiceDescriptors.cs @@ -87,7 +87,7 @@ internal static string GetQualifiedServiceName(Type serviceInterface) return new(serviceInterface, (descriptor32, descriptor64)); } - public static ServiceRpcDescriptor GetServiceDescriptor(Type serviceType, bool isRemoteHost64Bit) + public static ServiceJsonRpcDescriptor GetServiceDescriptor(Type serviceType, bool isRemoteHost64Bit) { var (descriptor32, descriptor64) = Descriptors[serviceType]; return isRemoteHost64Bit ? descriptor64 : descriptor32; diff --git a/src/Workspaces/Remote/ServiceHub/Services/BrokeredServiceBase.FactoryBase.cs b/src/Workspaces/Remote/ServiceHub/Services/BrokeredServiceBase.FactoryBase.cs index 429f4a2ebe2d0..701f06c0cf5b2 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/BrokeredServiceBase.FactoryBase.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/BrokeredServiceBase.FactoryBase.cs @@ -71,6 +71,13 @@ internal TService Create( { var descriptor = ServiceDescriptors.GetServiceDescriptor(typeof(TService), isRemoteHost64Bit: IntPtr.Size == 8); var serviceHubTraceSource = (TraceSource)hostProvidedServices.GetService(typeof(TraceSource)); + + if (descriptor.MultiplexingStreamOptions is not null && serviceHubTraceSource is not null) + { + descriptor = ((ServiceDescriptor)descriptor).WithMultiplexingStream( + new MultiplexingStream.Options(descriptor.MultiplexingStreamOptions) { TraceSource = serviceHubTraceSource }); + } + var serverConnection = descriptor.WithTraceSource(serviceHubTraceSource).ConstructRpcConnection(pipe); var args = new ServiceConstructionArguments(hostProvidedServices, serviceBroker);