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

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

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

H
Heejae Chang 已提交
25
        public static async Task<RemoteHostClient> CreateAsync(Workspace workspace, bool runCacheCleanup, CancellationToken cancellationToken)
26
        {
H
Heejae Chang 已提交
27
            var inprocServices = new InProcRemoteServices(runCacheCleanup);
28

29
            // 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
30
            var remotableDataRpc = new RemotableDataJsonRpc(workspace, inprocServices.Logger, await inprocServices.RequestServiceAsync(WellKnownServiceHubServices.SnapshotService, cancellationToken).ConfigureAwait(false));
31
            var remoteHostStream = await inprocServices.RequestServiceAsync(WellKnownRemoteHostServices.RemoteHostService, cancellationToken).ConfigureAwait(false);
32

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

            // make sure connection is done right
37
            var telemetrySession = default(string);
38 39 40 41
            var uiCultureLCIDE = 0;
            var cultureLCID = 0;

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

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

46
            instance.Started();
47 48 49 50 51

            // return instance
            return instance;
        }

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

62 63
            ClientId = clientId;

64
            _inprocServices = inprocServices;
65
            _remotableDataRpc = remotableDataRpc;
66

67
            _rpc = stream.CreateStreamJsonRpc(target: this, inprocServices.Logger);
68 69 70

            // handle disconnected situation
            _rpc.Disconnected += OnRpcDisconnected;
71 72

            _rpc.StartListening();
73 74
        }

75 76
        public AssetStorage AssetStorage => _inprocServices.AssetStorage;

77 78 79 80 81
        public void RegisterService(string name, Func<Stream, IServiceProvider, ServiceHubServiceBase> serviceCreator)
        {
            _inprocServices.RegisterService(name, serviceCreator);
        }

82 83
        public override string ClientId { get; }

84
        public override async Task<Connection> TryCreateConnectionAsync(
H
Heejae Chang 已提交
85
            string serviceName, object callbackTarget, CancellationToken cancellationToken)
86
        {
H
Heejae Chang 已提交
87
            // get stream from service hub to communicate service specific information 
88 89 90
            // this is what consumer actually use to communicate information
            var serviceStream = await _inprocServices.RequestServiceAsync(serviceName, cancellationToken).ConfigureAwait(false);

91
            return new JsonRpcConnection(_inprocServices.Logger, callbackTarget, serviceStream, _remotableDataRpc.TryAddReference());
92 93
        }

94
        protected override void OnStarted()
95 96 97
        {
        }

98
        protected override void OnStopped()
99
        {
100 101
            // we are asked to disconnect. unsubscribe and dispose to disconnect
            _rpc.Disconnected -= OnRpcDisconnected;
102
            _rpc.Dispose();
103
            _remotableDataRpc.Dispose();
104 105 106 107
        }

        private void OnRpcDisconnected(object sender, JsonRpcDisconnectedEventArgs e)
        {
108
            Stopped();
109 110
        }

111 112 113 114 115 116
        public class ServiceProvider : IServiceProvider
        {
            private static readonly TraceSource s_traceSource = new TraceSource("inprocRemoteClient");

            private readonly AssetStorage _storage;

H
Heejae Chang 已提交
117
            public ServiceProvider(bool runCacheCleanup)
118
            {
H
Heejae Chang 已提交
119
                _storage = runCacheCleanup ?
120
                    new AssetStorage(cleanupInterval: TimeSpan.FromSeconds(30), purgeAfter: TimeSpan.FromMinutes(1), gcAfter: TimeSpan.FromMinutes(5)) :
H
Heejae Chang 已提交
121
                    new AssetStorage();
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
            }

            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);
            }
        }

142 143
        private class InProcRemoteServices
        {
144
            private readonly ServiceProvider _serviceProvider;
145
            private readonly Dictionary<string, Func<Stream, IServiceProvider, ServiceHubServiceBase>> _creatorMap;
146

H
Heejae Chang 已提交
147
            public InProcRemoteServices(bool runCacheCleanup)
148
            {
H
Heejae Chang 已提交
149
                _serviceProvider = new ServiceProvider(runCacheCleanup);
150 151 152 153 154 155
                _creatorMap = new Dictionary<string, Func<Stream, IServiceProvider, ServiceHubServiceBase>>();

                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));
156 157
            }

158
            public AssetStorage AssetStorage => _serviceProvider.AssetStorage;
159
            public TraceSource Logger { get; } = new TraceSource("Default");
160

161 162 163 164 165
            public void RegisterService(string name, Func<Stream, IServiceProvider, ServiceHubServiceBase> serviceCreator)
            {
                _creatorMap.Add(name, serviceCreator);
            }

166 167
            public Task<Stream> RequestServiceAsync(string serviceName, CancellationToken cancellationToken)
            {
168 169
                Func<Stream, IServiceProvider, ServiceHubServiceBase> creator;
                if (_creatorMap.TryGetValue(serviceName, out creator))
170
                {
171 172
                    var tuple = FullDuplexStream.CreateStreams();
                    return Task.FromResult<Stream>(new WrappedStream(creator(tuple.Item1, _serviceProvider), tuple.Item2));
173 174 175 176 177 178 179 180 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
                }

                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 已提交
263
}