InProcRemostHostClient.cs 11.6 KB
Newer Older
1 2
// Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.

3 4
#nullable enable

5
using System;
6
using System.Collections.Generic;
7 8 9 10 11 12 13
using System.Diagnostics;
using System.IO;
using System.Runtime.Remoting;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Remote;
14
using Microsoft.CodeAnalysis.Remote.Services;
15 16 17 18 19
using Microsoft.VisualStudio.LanguageServices.Remote;
using Nerdbank;
using Roslyn.Utilities;
using StreamJsonRpc;

20
namespace Roslyn.Test.Utilities.Remote
21
{
22
    internal sealed class InProcRemoteHostClient : RemoteHostClient
23 24
    {
        private readonly InProcRemoteServices _inprocServices;
H
Heejae Chang 已提交
25
        private readonly ReferenceCountedDisposable<RemotableDataJsonRpc> _remotableDataRpc;
26 27
        private readonly JsonRpc _rpc;

C
Cyrus Najmabadi 已提交
28
        public static async Task<RemoteHostClient> CreateAsync(Workspace workspace, bool runCacheCleanup)
29
        {
H
Heejae Chang 已提交
30
            var inprocServices = new InProcRemoteServices(runCacheCleanup);
31

32
            // Create the RemotableDataJsonRpc before we create the remote host: this call implicitly sets up the remote IExperimentationService so that will be available for later calls
C
Cyrus Najmabadi 已提交
33 34
            var remotableDataRpc = new RemotableDataJsonRpc(workspace, inprocServices.Logger, await inprocServices.RequestServiceAsync(WellKnownServiceHubServices.SnapshotService).ConfigureAwait(false));
            var remoteHostStream = await inprocServices.RequestServiceAsync(WellKnownRemoteHostServices.RemoteHostService).ConfigureAwait(false);
35

36 37
            var current = CreateClientId(Process.GetCurrentProcess().Id.ToString());
            var instance = new InProcRemoteHostClient(current, workspace, inprocServices, new ReferenceCountedDisposable<RemotableDataJsonRpc>(remotableDataRpc), remoteHostStream);
38 39

            // make sure connection is done right
40
            var telemetrySession = default(string);
41 42 43 44
            var uiCultureLCIDE = 0;
            var cultureLCID = 0;

            var host = await instance._rpc.InvokeAsync<string>(nameof(IRemoteHostService.Connect), current, uiCultureLCIDE, cultureLCID, telemetrySession).ConfigureAwait(false);
45 46 47 48

            // TODO: change this to non fatal watson and make VS to use inproc implementation
            Contract.ThrowIfFalse(host == current.ToString());

49
            instance.Started();
50 51 52 53 54

            // return instance
            return instance;
        }

55
        private InProcRemoteHostClient(
56
            string clientId,
57 58
            Workspace workspace,
            InProcRemoteServices inprocServices,
H
Heejae Chang 已提交
59
            ReferenceCountedDisposable<RemotableDataJsonRpc> remotableDataRpc,
60 61
            Stream stream)
            : base(workspace)
62
        {
H
Heejae Chang 已提交
63 64
            Contract.ThrowIfNull(remotableDataRpc);

65 66
            ClientId = clientId;

67
            _inprocServices = inprocServices;
68
            _remotableDataRpc = remotableDataRpc;
69

70
            _rpc = stream.CreateStreamJsonRpc(target: this, inprocServices.Logger);
71 72 73

            // handle disconnected situation
            _rpc.Disconnected += OnRpcDisconnected;
74 75

            _rpc.StartListening();
76 77
        }

78 79
        public AssetStorage AssetStorage => _inprocServices.AssetStorage;

80 81 82 83 84
        public void RegisterService(string name, Func<Stream, IServiceProvider, ServiceHubServiceBase> serviceCreator)
        {
            _inprocServices.RegisterService(name, serviceCreator);
        }

H
HeeJae Chang 已提交
85 86 87 88 89
        public Task<Stream> RequestServiceAsync(string serviceName)
        {
            return _inprocServices.RequestServiceAsync(serviceName);
        }

90 91
        public override string ClientId { get; }

92 93
        public override async Task<Connection?> TryCreateConnectionAsync(
            string serviceName, object? callbackTarget, CancellationToken cancellationToken)
94
        {
H
Heejae Chang 已提交
95
            // get stream from service hub to communicate service specific information 
96
            // this is what consumer actually use to communicate information
C
Cyrus Najmabadi 已提交
97
            var serviceStream = await _inprocServices.RequestServiceAsync(serviceName).ConfigureAwait(false);
98

99
            return new JsonRpcConnection(_inprocServices.Logger, callbackTarget, serviceStream, _remotableDataRpc.TryAddReference() ?? throw new ObjectDisposedException(GetType().FullName));
100 101
        }

102
        protected override void OnStarted()
103 104 105
        {
        }

106
        protected override void OnStopped()
107
        {
108 109
            // we are asked to disconnect. unsubscribe and dispose to disconnect
            _rpc.Disconnected -= OnRpcDisconnected;
110
            _rpc.Dispose();
111
            _remotableDataRpc.Dispose();
112 113 114 115
        }

        private void OnRpcDisconnected(object sender, JsonRpcDisconnectedEventArgs e)
        {
116
            Stopped();
117 118
        }

119 120 121 122 123 124
        public class ServiceProvider : IServiceProvider
        {
            private static readonly TraceSource s_traceSource = new TraceSource("inprocRemoteClient");

            private readonly AssetStorage _storage;

H
Heejae Chang 已提交
125
            public ServiceProvider(bool runCacheCleanup)
126
            {
H
Heejae Chang 已提交
127
                _storage = runCacheCleanup ?
128
                    new AssetStorage(cleanupInterval: TimeSpan.FromSeconds(30), purgeAfter: TimeSpan.FromMinutes(1), gcAfter: TimeSpan.FromMinutes(5)) :
H
Heejae Chang 已提交
129
                    new AssetStorage();
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
            }

            public AssetStorage AssetStorage => _storage;

            public object GetService(Type serviceType)
            {
                if (typeof(TraceSource) == serviceType)
                {
                    return s_traceSource;
                }

                if (typeof(AssetStorage) == serviceType)
                {
                    return _storage;
                }

                throw ExceptionUtilities.UnexpectedValue(serviceType);
            }
        }

150 151
        private class InProcRemoteServices
        {
152
            private readonly ServiceProvider _serviceProvider;
153
            private readonly Dictionary<string, Func<Stream, IServiceProvider, ServiceBase>> _creatorMap;
154

H
Heejae Chang 已提交
155
            public InProcRemoteServices(bool runCacheCleanup)
156
            {
H
Heejae Chang 已提交
157
                _serviceProvider = new ServiceProvider(runCacheCleanup);
158
                _creatorMap = new Dictionary<string, Func<Stream, IServiceProvider, ServiceBase>>();
159 160 161 162 163

                RegisterService(WellKnownRemoteHostServices.RemoteHostService, (s, p) => new RemoteHostService(s, p));
                RegisterService(WellKnownServiceHubServices.CodeAnalysisService, (s, p) => new CodeAnalysisService(s, p));
                RegisterService(WellKnownServiceHubServices.SnapshotService, (s, p) => new SnapshotService(s, p));
                RegisterService(WellKnownServiceHubServices.RemoteSymbolSearchUpdateEngine, (s, p) => new RemoteSymbolSearchUpdateEngine(s, p));
164
                RegisterService(WellKnownServiceHubServices.LanguageServer, (s, p) => new LanguageServer(s, p));
165 166
            }

167
            public AssetStorage AssetStorage => _serviceProvider.AssetStorage;
168
            public TraceSource Logger { get; } = new TraceSource("Default");
169

170
            public void RegisterService(string name, Func<Stream, IServiceProvider, ServiceBase> serviceCreator)
171 172 173 174
            {
                _creatorMap.Add(name, serviceCreator);
            }

C
Cyrus Najmabadi 已提交
175
            public Task<Stream> RequestServiceAsync(string serviceName)
176
            {
C
Cyrus Najmabadi 已提交
177
                if (_creatorMap.TryGetValue(serviceName, out var creator))
178
                {
179 180
                    var tuple = FullDuplexStream.CreateStreams();
                    return Task.FromResult<Stream>(new WrappedStream(creator(tuple.Item1, _serviceProvider), tuple.Item2));
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270
                }

                throw ExceptionUtilities.UnexpectedValue(serviceName);
            }

            private class WrappedStream : Stream
            {
                private readonly IDisposable _service;
                private readonly Stream _stream;

                public WrappedStream(IDisposable service, Stream stream)
                {
                    // tie service's lifetime with that of stream
                    _service = service;
                    _stream = stream;
                }

                public override long Position
                {
                    get { return _stream.Position; }
                    set { _stream.Position = value; }
                }

                public override int ReadTimeout
                {
                    get { return _stream.ReadTimeout; }
                    set { _stream.ReadTimeout = value; }
                }

                public override int WriteTimeout
                {
                    get { return _stream.WriteTimeout; }
                    set { _stream.WriteTimeout = value; }
                }

                public override bool CanRead => _stream.CanRead;
                public override bool CanSeek => _stream.CanSeek;
                public override bool CanWrite => _stream.CanWrite;
                public override long Length => _stream.Length;
                public override bool CanTimeout => _stream.CanTimeout;

                public override void Flush() => _stream.Flush();
                public override Task FlushAsync(CancellationToken cancellationToken) => _stream.FlushAsync(cancellationToken);

                public override long Seek(long offset, SeekOrigin origin) => _stream.Seek(offset, origin);
                public override void SetLength(long value) => _stream.SetLength(value);

                public override int ReadByte() => _stream.ReadByte();
                public override void WriteByte(byte value) => _stream.WriteByte(value);

                public override int Read(byte[] buffer, int offset, int count) => _stream.Read(buffer, offset, count);
                public override void Write(byte[] buffer, int offset, int count) => _stream.Write(buffer, offset, count);

                public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) => _stream.ReadAsync(buffer, offset, count, cancellationToken);
                public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) => _stream.WriteAsync(buffer, offset, count, cancellationToken);

                public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state) => _stream.BeginRead(buffer, offset, count, callback, state);
                public override int EndRead(IAsyncResult asyncResult) => _stream.EndRead(asyncResult);

                public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) => _stream.BeginWrite(buffer, offset, count, callback, state);
                public override void EndWrite(IAsyncResult asyncResult) => _stream.EndWrite(asyncResult);

                public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) => _stream.CopyToAsync(destination, bufferSize, cancellationToken);

                public override object InitializeLifetimeService()
                {
                    throw new NotSupportedException();
                }

                public override ObjRef CreateObjRef(Type requestedType)
                {
                    throw new NotSupportedException();
                }

                public override void Close()
                {
                    _service.Dispose();
                    _stream.Close();
                }

                protected override void Dispose(bool disposing)
                {
                    base.Dispose(disposing);

                    _service.Dispose();
                    _stream.Dispose();
                }
            }
        }
    }
S
Sam Harwell 已提交
271
}