public async Task <Stream> AcceptIpcStreamAsync(CancellationToken token)
        {
            Stream ipcServerStream = null;

            _logger?.LogDebug($"Waiting for new ipc connection at endpoint \"{_ipcServerPath}\".");


            using var connectTimeoutTokenSource = new CancellationTokenSource();
            using var connectTokenSource        = CancellationTokenSource.CreateLinkedTokenSource(token, connectTimeoutTokenSource.Token);

            try
            {
                connectTimeoutTokenSource.CancelAfter(IpcServerTimeoutMs);
                ipcServerStream = await _ipcServer.AcceptAsync(connectTokenSource.Token).ConfigureAwait(false);
            }
            catch (Exception)
            {
                ipcServerStream?.Dispose();

                if (connectTimeoutTokenSource.IsCancellationRequested)
                {
                    _logger?.LogDebug("No ipc stream connected, timing out.");
                    throw new TimeoutException();
                }

                throw;
            }

            if (ipcServerStream != null)
            {
                _logger?.LogDebug("Successfully connected ipc stream.");
            }

            return(ipcServerStream);
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Provides endpoint information when a new runtime instance connects to the server.
        /// </summary>
        /// <param name="token">The token to monitor for cancellation requests.</param>
        /// <returns>A <see cref="IpcEndpointInfo"/> that contains information about the new runtime instance connection.</returns>
        /// <remarks>
        /// This will only provide endpoint information on the first time a runtime connects to the server.
        /// If a connection is removed using <see cref="RemoveConnection(Guid)"/> and the same runtime instance,
        /// reconnects after this call, then a new <see cref="IpcEndpointInfo"/> will be produced.
        /// </remarks>
        public async Task <IpcEndpointInfo> AcceptAsync(CancellationToken token)
        {
            VerifyNotDisposed();

            while (true)
            {
                Stream       stream    = null;
                IpcAdvertise advertise = null;
                try
                {
                    stream = await _transport.AcceptAsync(token).ConfigureAwait(false);
                }
                catch (Exception ex) when(!(ex is OperationCanceledException))
                {
                    // The advertise data could be incomplete if the runtime shuts down before completely writing
                    // the information. Catch the exception and continue waiting for a new connection.
                }

                token.ThrowIfCancellationRequested();

                if (null != stream)
                {
                    // Cancel parsing of advertise data after timeout period to
                    // mitigate runtimes that write partial data and do not close the stream (avoid waiting forever).
                    using var parseCancellationSource = new CancellationTokenSource();
                    using var linkedSource            = CancellationTokenSource.CreateLinkedTokenSource(token, parseCancellationSource.Token);
                    try
                    {
                        parseCancellationSource.CancelAfter(ParseAdvertiseTimeout);

                        advertise = await IpcAdvertise.ParseAsync(stream, linkedSource.Token).ConfigureAwait(false);
                    }
                    catch (OperationCanceledException) when(parseCancellationSource.IsCancellationRequested)
                    {
                        // Only handle cancellation if it was due to the parse timeout.
                    }
                    catch (Exception ex) when(!(ex is OperationCanceledException))
                    {
                        // Catch all other exceptions and continue waiting for a new connection.
                    }
                }

                token.ThrowIfCancellationRequested();

                if (null != advertise)
                {
                    Guid runtimeCookie = advertise.RuntimeInstanceCookie;
                    int  pid           = unchecked ((int)advertise.ProcessId);

                    lock (_lock)
                    {
                        ProvideStream(runtimeCookie, stream);
                        // Consumers should hold onto the endpoint info and use it for diagnostic communication,
                        // regardless of the number of times the same runtime instance connects. This requires consumers
                        // to continuously invoke the AcceptAsync method in order to handle runtime instance reconnects,
                        // even if the consumer only wants to handle a single endpoint.
                        if (!_cachedEndpoints.ContainsKey(runtimeCookie))
                        {
                            ServerIpcEndpoint endpoint = new ServerIpcEndpoint(this, runtimeCookie);
                            _cachedEndpoints.Add(runtimeCookie, endpoint);
                            return(new IpcEndpointInfo(endpoint, pid, runtimeCookie));
                        }
                    }
                }

                token.ThrowIfCancellationRequested();
            }
        }