InProcRemostHostClient.cs 10.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// 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;
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;

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

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

            var remoteHostStream = await inprocServices.RequestServiceAsync(WellKnownRemoteHostServices.RemoteHostService, cancellationToken).ConfigureAwait(false);
H
Heejae Chang 已提交
29
            var remotableDataRpc = new RemotableDataJsonRpc(workspace, await inprocServices.RequestServiceAsync(WellKnownServiceHubServices.SnapshotService, cancellationToken).ConfigureAwait(false));
30

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

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

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

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

            // return instance
            return instance;
        }

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

56
            _inprocServices = inprocServices;
57
            _remotableDataRpc = remotableDataRpc;
58

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

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

            _rpc.StartListening();
66 67
        }

68 69
        public AssetStorage AssetStorage => _inprocServices.AssetStorage;

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

77
            return new JsonRpcConnection(callbackTarget, serviceStream, _remotableDataRpc.TryAddReference());
78 79
        }

80
        protected override void OnStarted()
81 82 83
        {
        }

84
        protected override void OnStopped()
85
        {
86 87
            // we are asked to disconnect. unsubscribe and dispose to disconnect
            _rpc.Disconnected -= OnRpcDisconnected;
88
            _rpc.Dispose();
89
            _remotableDataRpc.Dispose();
90 91 92 93
        }

        private void OnRpcDisconnected(object sender, JsonRpcDisconnectedEventArgs e)
        {
94
            Stopped();
95 96
        }

97 98 99 100 101 102
        public class ServiceProvider : IServiceProvider
        {
            private static readonly TraceSource s_traceSource = new TraceSource("inprocRemoteClient");

            private readonly AssetStorage _storage;

H
Heejae Chang 已提交
103
            public ServiceProvider(bool runCacheCleanup)
104
            {
H
Heejae Chang 已提交
105 106 107
                _storage = runCacheCleanup ?
                    new AssetStorage(cleanupInterval: TimeSpan.FromSeconds(30), purgeAfter: TimeSpan.FromMinutes(1)) :
                    new AssetStorage();
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
            }

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

128 129
        private class InProcRemoteServices
        {
130
            private readonly ServiceProvider _serviceProvider;
131

H
Heejae Chang 已提交
132
            public InProcRemoteServices(bool runCacheCleanup)
133
            {
H
Heejae Chang 已提交
134
                _serviceProvider = new ServiceProvider(runCacheCleanup);
135 136
            }

137 138
            public AssetStorage AssetStorage => _serviceProvider.AssetStorage;

139 140 141 142 143 144 145
            public Task<Stream> RequestServiceAsync(string serviceName, CancellationToken cancellationToken)
            {
                switch (serviceName)
                {
                    case WellKnownRemoteHostServices.RemoteHostService:
                        {
                            var tuple = FullDuplexStream.CreateStreams();
146
                            return Task.FromResult<Stream>(new WrappedStream(new RemoteHostService(tuple.Item1, _serviceProvider), tuple.Item2));
147
                        }
148
                    case WellKnownServiceHubServices.CodeAnalysisService:
149 150
                        {
                            var tuple = FullDuplexStream.CreateStreams();
151
                            return Task.FromResult<Stream>(new WrappedStream(new CodeAnalysisService(tuple.Item1, _serviceProvider), tuple.Item2));
152
                        }
153
                    case WellKnownServiceHubServices.SnapshotService:
154 155
                        {
                            var tuple = FullDuplexStream.CreateStreams();
156
                            return Task.FromResult<Stream>(new WrappedStream(new SnapshotService(tuple.Item1, _serviceProvider), tuple.Item2));
157
                        }
158
                    case WellKnownServiceHubServices.RemoteSymbolSearchUpdateEngine:
159 160
                        {
                            var tuple = FullDuplexStream.CreateStreams();
161
                            return Task.FromResult<Stream>(new WrappedStream(new RemoteSymbolSearchUpdateEngine(tuple.Item1, _serviceProvider), tuple.Item2));
162
                        }
163 164 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
                }

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