InProcRemostHostClient.cs 11.1 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

            var remoteHostStream = await inprocServices.RequestServiceAsync(WellKnownRemoteHostServices.RemoteHostService, cancellationToken).ConfigureAwait(false);
30
            var remotableDataRpc = new RemotableDataJsonRpc(workspace, inprocServices.Logger, await inprocServices.RequestServiceAsync(WellKnownServiceHubServices.SnapshotService, cancellationToken).ConfigureAwait(false));
31

H
Heejae Chang 已提交
32
            var instance = new InProcRemoteHostClient(workspace, inprocServices, new ReferenceCountedDisposable<RemotableDataJsonRpc>(remotableDataRpc), remoteHostStream);
33 34 35

            // make sure connection is done right
            var current = $"VS ({Process.GetCurrentProcess().Id})";
36
            var telemetrySession = default(string);
37
            var host = await instance._rpc.InvokeAsync<string>(nameof(IRemoteHostService.Connect), current, telemetrySession).ConfigureAwait(false);
38 39 40 41

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

42
            instance.Started();
43 44 45 46 47

            // return instance
            return instance;
        }

48 49 50
        private InProcRemoteHostClient(
            Workspace workspace,
            InProcRemoteServices inprocServices,
H
Heejae Chang 已提交
51
            ReferenceCountedDisposable<RemotableDataJsonRpc> remotableDataRpc,
52
            Stream stream) :
53 54
            base(workspace)
        {
H
Heejae Chang 已提交
55 56
            Contract.ThrowIfNull(remotableDataRpc);

57
            _inprocServices = inprocServices;
58
            _remotableDataRpc = remotableDataRpc;
59

60
            _rpc = new JsonRpc(new JsonRpcMessageHandler(stream, stream), target: this);
61
            _rpc.JsonSerializer.Converters.Add(AggregateJsonConverter.Instance);
62 63 64

            // handle disconnected situation
            _rpc.Disconnected += OnRpcDisconnected;
65 66

            _rpc.StartListening();
67 68
        }

69 70
        public AssetStorage AssetStorage => _inprocServices.AssetStorage;

71 72 73 74 75
        public void RegisterService(string name, Func<Stream, IServiceProvider, ServiceHubServiceBase> serviceCreator)
        {
            _inprocServices.RegisterService(name, serviceCreator);
        }

76
        public override async Task<Connection> TryCreateConnectionAsync(
H
Heejae Chang 已提交
77
            string serviceName, object callbackTarget, CancellationToken cancellationToken)
78
        {
H
Heejae Chang 已提交
79
            // get stream from service hub to communicate service specific information 
80 81 82
            // this is what consumer actually use to communicate information
            var serviceStream = await _inprocServices.RequestServiceAsync(serviceName, cancellationToken).ConfigureAwait(false);

83
            return new JsonRpcConnection(_inprocServices.Logger, callbackTarget, serviceStream, _remotableDataRpc.TryAddReference());
84 85
        }

86
        protected override void OnStarted()
87 88 89
        {
        }

90
        protected override void OnStopped()
91
        {
92 93
            // we are asked to disconnect. unsubscribe and dispose to disconnect
            _rpc.Disconnected -= OnRpcDisconnected;
94
            _rpc.Dispose();
95
            _remotableDataRpc.Dispose();
96 97 98 99
        }

        private void OnRpcDisconnected(object sender, JsonRpcDisconnectedEventArgs e)
        {
100
            Stopped();
101 102
        }

103 104 105 106 107 108
        public class ServiceProvider : IServiceProvider
        {
            private static readonly TraceSource s_traceSource = new TraceSource("inprocRemoteClient");

            private readonly AssetStorage _storage;

H
Heejae Chang 已提交
109
            public ServiceProvider(bool runCacheCleanup)
110
            {
H
Heejae Chang 已提交
111
                _storage = runCacheCleanup ?
112
                    new AssetStorage(cleanupInterval: TimeSpan.FromSeconds(30), purgeAfter: TimeSpan.FromMinutes(1), gcAfter: TimeSpan.FromMinutes(5)) :
H
Heejae Chang 已提交
113
                    new AssetStorage();
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
            }

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

134 135
        private class InProcRemoteServices
        {
136
            private readonly ServiceProvider _serviceProvider;
137
            private readonly Dictionary<string, Func<Stream, IServiceProvider, ServiceHubServiceBase>> _creatorMap;
138

H
Heejae Chang 已提交
139
            public InProcRemoteServices(bool runCacheCleanup)
140
            {
H
Heejae Chang 已提交
141
                _serviceProvider = new ServiceProvider(runCacheCleanup);
142 143 144 145 146 147
                _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));
148 149
            }

150
            public AssetStorage AssetStorage => _serviceProvider.AssetStorage;
151
            public TraceSource Logger { get; } = new TraceSource("Default");
152

153 154 155 156 157
            public void RegisterService(string name, Func<Stream, IServiceProvider, ServiceHubServiceBase> serviceCreator)
            {
                _creatorMap.Add(name, serviceCreator);
            }

158 159
            public Task<Stream> RequestServiceAsync(string serviceName, CancellationToken cancellationToken)
            {
160 161
                Func<Stream, IServiceProvider, ServiceHubServiceBase> creator;
                if (_creatorMap.TryGetValue(serviceName, out creator))
162
                {
163 164
                    var tuple = FullDuplexStream.CreateStreams();
                    return Task.FromResult<Stream>(new WrappedStream(creator(tuple.Item1, _serviceProvider), tuple.Item2));
165 166 167 168 169 170 171 172 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
                }

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