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