private static async Task ListenCoreAsync(
            string pipeName,
            AsyncQueue <ListenResult> queue,
            Func <string> getClientLoggingIdentifier,
            CancellationToken cancellationToken)
        {
            while (!cancellationToken.IsCancellationRequested)
            {
                NamedPipeServerStream?pipeStream = null;

                try
                {
                    // Create the pipe and begin waiting for a connection. This
                    // doesn't block, but could fail in certain circumstances, such
                    // as Windows refusing to create the pipe for some reason
                    // (out of handles?), or the pipe was disconnected before we
                    // starting listening
                    CompilerServerLogger.Log($"Constructing pipe and waiting for connections '{pipeName}'");
                    pipeStream = NamedPipeUtil.CreateServer(pipeName);

                    // The WaitForConnectionAsync API does not fully respect the provided CancellationToken
                    // on all platforms:
                    //
                    //  https://github.com/dotnet/runtime/issues/40289
                    //
                    // To mitigate this we need to setup a cancellation Task and dispose the NamedPipeServerStream
                    // if it ever completes. Once all of the NamedPipeServerStream for the given pipe name are
                    // disposed they will all exit the WaitForConnectionAsync method
                    var connectTask = pipeStream.WaitForConnectionAsync(cancellationToken);
                    if (!PlatformInformation.IsWindows)
                    {
                        var cancelTask    = Task.Delay(TimeSpan.FromMilliseconds(-1), cancellationToken);
                        var completedTask = await Task.WhenAny(new[] { connectTask, cancelTask }).ConfigureAwait(false);

                        if (completedTask == cancelTask)
                        {
                            throw new OperationCanceledException();
                        }
                    }

                    await connectTask.ConfigureAwait(false);

                    CompilerServerLogger.Log("Pipe connection established.");
                    var connection = new NamedPipeClientConnection(pipeStream, getClientLoggingIdentifier());
                    queue.Enqueue(new ListenResult(connection: connection));
                }
                catch (OperationCanceledException)
                {
                    // Expected when the host is shutting down.
                    CompilerServerLogger.Log($"Pipe connection cancelled");
                    pipeStream?.Dispose();
                }
                catch (Exception ex)
                {
                    CompilerServerLogger.LogException(ex, $"Pipe connection error");
                    queue.Enqueue(new ListenResult(exception: ex));
                    pipeStream?.Dispose();
                }
            }
        }
Exemplo n.º 2
0
        /// <summary>
        /// Creates a Task representing the processing of the new connection.  Returns null
        /// if the server is unable to create a new Task object for the connection.
        /// </summary>
        private async Task <CompletionReason> CreateHandleConnectionTask(Task <NamedPipeServerStream> pipeStreamTask, TaskCompletionSource <TimeSpan?> changeKeepAliveSource, CancellationToken cancellationToken)
        {
            var pipeStream = await pipeStreamTask.ConfigureAwait(false);

            var clientConnection = new NamedPipeClientConnection(pipeStream);
            var connection       = new Connection(clientConnection, _handler);

            return(await connection.ServeConnection(changeKeepAliveSource, cancellationToken).ConfigureAwait(false));
        }
Exemplo n.º 3
0
        /// <summary>
        /// Creates a Task representing the processing of the new connection.  This will return a task that
        /// will never fail.  It will always produce a <see cref="ConnectionData"/> value.  Connection errors
        /// will end up being represented as <see cref="CompletionReason.ClientDisconnect"/>
        /// </summary>
        internal static async Task <ConnectionData> CreateHandleConnectionTask(Task <NamedPipeServerStream> pipeStreamTask, IRequestHandler handler, CancellationToken cancellationToken)
        {
            Connection connection;

            try
            {
                var pipeStream = await pipeStreamTask.ConfigureAwait(false);

                var clientConnection = new NamedPipeClientConnection(pipeStream);
                connection = new Connection(clientConnection, handler);
            }
            catch (Exception ex)
            {
                // Unable to establish a connection with the client.  The client is responsible for
                // handling this case.  Nothing else for us to do here.
                CompilerServerLogger.LogException(ex, "Error creating client named pipe");
                return(new ConnectionData(CompletionReason.CompilationNotStarted));
            }

            return(await connection.ServeConnection(cancellationToken).ConfigureAwait(false));
        }
Exemplo n.º 4
0
        /// <summary>
        /// Creates a Task representing the processing of the new connection.  This will return a task that
        /// will never fail.  It will always produce a <see cref="ConnectionData"/> value.  Connection errors
        /// will end up being represented as <see cref="CompletionReason.ClientDisconnect"/>
        /// </summary>
        internal static async Task<ConnectionData> CreateHandleConnectionTask(Task<NamedPipeServerStream> pipeStreamTask, IRequestHandler handler, CancellationToken cancellationToken)
        {
            Connection connection;
            try
            {
                var pipeStream = await pipeStreamTask.ConfigureAwait(false);
                var clientConnection = new NamedPipeClientConnection(pipeStream);
                connection = new Connection(clientConnection, handler);
            }
            catch (Exception ex)
            {
                // Unable to establish a connection with the client.  The client is responsible for
                // handling this case.  Nothing else for us to do here.
                CompilerServerLogger.LogException(ex, "Error creating client named pipe");
                return new ConnectionData(CompletionReason.CompilationNotStarted);
            }

            return await connection.ServeConnection(cancellationToken).ConfigureAwait(false);
        }
Exemplo n.º 5
0
 /// <summary>
 /// Creates a Task representing the processing of the new connection.  Returns null 
 /// if the server is unable to create a new Task object for the connection.  
 /// </summary>
 private async Task<CompletionReason> CreateHandleConnectionTask(Task<NamedPipeServerStream> pipeStreamTask, TaskCompletionSource<TimeSpan?> changeKeepAliveSource, CancellationToken cancellationToken)
 {
     var pipeStream = await pipeStreamTask.ConfigureAwait(false);
     var clientConnection = new NamedPipeClientConnection(pipeStream);
     var connection = new Connection(clientConnection, _handler);
     return await connection.ServeConnection(changeKeepAliveSource, cancellationToken).ConfigureAwait(false);
 }