/// <summary> /// Try to process the request using the server. Returns a null-containing Task if a response /// from the server cannot be retrieved. /// </summary> private static async Task <ServerResponse> TryProcessRequest( Client client, ServerRequest request, CancellationToken cancellationToken) { ServerResponse response; using (client) { // Write the request try { ServerLogger.Log("Begin writing request"); await request.WriteAsync(client.Stream, cancellationToken).ConfigureAwait(false); ServerLogger.Log("End writing request"); } catch (Exception e) { ServerLogger.LogException(e, "Error writing build request."); return(new RejectedServerResponse()); } // Wait for the compilation and a monitor to detect if the server disconnects var serverCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); ServerLogger.Log("Begin reading response"); var responseTask = ServerResponse.ReadAsync(client.Stream, serverCts.Token); var monitorTask = client.WaitForDisconnectAsync(serverCts.Token); await Task.WhenAny(new[] { responseTask, monitorTask }).ConfigureAwait(false); ServerLogger.Log("End reading response"); if (responseTask.IsCompleted) { // await the task to log any exceptions try { response = await responseTask.ConfigureAwait(false); } catch (Exception e) { ServerLogger.LogException(e, "Error reading response"); response = new RejectedServerResponse(); } } else { ServerLogger.Log("Server disconnect"); response = new RejectedServerResponse(); } // Cancel whatever task is still around serverCts.Cancel(); Debug.Assert(response != null); return(response); } }
internal async Task <ConnectionResult> AcceptConnection(Task <Connection> task, bool accept, CancellationToken cancellationToken) { Connection connection; try { connection = await task; } 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. ServerLogger.LogException(ex, "Error creating client named pipe"); return(new ConnectionResult(ConnectionResult.Reason.CompilationNotStarted)); } try { using (connection) { ServerRequest request; try { ServerLogger.Log("Begin reading request."); request = await ServerRequest.ReadAsync(connection.Stream, cancellationToken).ConfigureAwait(false); ServerLogger.Log("End reading request."); } catch (Exception e) { ServerLogger.LogException(e, "Error reading build request."); return(new ConnectionResult(ConnectionResult.Reason.CompilationNotStarted)); } if (request.IsShutdownRequest()) { // Reply with the PID of this process so that the client can wait for it to exit. var response = new ShutdownServerResponse(Process.GetCurrentProcess().Id); await response.WriteAsync(connection.Stream, cancellationToken); // We can safely disconnect the client, then when this connection gets cleaned up by the event loop // the server will go to a shutdown state. return(new ConnectionResult(ConnectionResult.Reason.ClientShutdownRequest)); } else if (!accept) { // We're already in shutdown mode, respond gracefully so the client can run in-process. var response = new RejectedServerResponse(); await response.WriteAsync(connection.Stream, cancellationToken).ConfigureAwait(false); return(new ConnectionResult(ConnectionResult.Reason.CompilationNotStarted)); } else { // If we get here then this is a real request that we will accept and process. // // Kick off both the compilation and a task to monitor the pipe for closing. var buildCancelled = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); var watcher = connection.WaitForDisconnectAsync(buildCancelled.Token); var worker = ExecuteRequestAsync(request, buildCancelled.Token); // await will end when either the work is complete or the connection is closed. await Task.WhenAny(worker, watcher); // Do an 'await' on the completed task, preference being compilation, to force // any exceptions to be realized in this method for logging. ConnectionResult.Reason reason; if (worker.IsCompleted) { var response = await worker; try { ServerLogger.Log("Begin writing response."); await response.WriteAsync(connection.Stream, cancellationToken); ServerLogger.Log("End writing response."); reason = ConnectionResult.Reason.CompilationCompleted; _eventBus.CompilationCompleted(); } catch { reason = ConnectionResult.Reason.ClientDisconnect; } } else { await watcher; reason = ConnectionResult.Reason.ClientDisconnect; } // Begin the tear down of the Task which didn't complete. buildCancelled.Cancel(); return(new ConnectionResult(reason, request.KeepAlive)); } } } catch (Exception ex) { ServerLogger.LogException(ex, "Error handling connection"); return(new ConnectionResult(ConnectionResult.Reason.ClientException)); } }