/// <summary> /// Called from the Connection class when the connection is complete. /// </summary> private void ConnectionCompleted(Connection connection) { Interlocked.Decrement(ref this.activeConnectionCount); if (this.serverDieTimeout == 0) { this.waitingPipeStream.Close(); Debug.Assert(this.dieTimeoutBlock != null); this.dieTimeoutBlock.Set(); } if (this.serverDieTimeout > 0 && this.activeConnectionCount == 0) { StartTimeoutTimerIfNecessary(); } CompilerServerLogger.Log("Removed connection {0}, {1} remaining.", connection.LoggingIdentifier, this.activeConnectionCount); }
/// <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<IClientConnection> clientConnectionTask, IRequestHandler handler, CancellationToken cancellationToken) { Connection connection; try { var clientConnection = await clientConnectionTask.ConfigureAwait(false); 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> /// Checks to see if memory is available, and if it is creates a new /// Connection object, awaits the completion of the connection, then /// runs <see cref="ConnectionCompleted"/> for cleanup. /// </summary> private async Task DispatchConnection(NamedPipeServerStream pipeStream) { try { // There is always a race between timeout and connections because // there is no way to cancel listening on the pipe without // closing the pipe. We immediately increment the connection // semaphore while processing connections in order to narrow // the race window as much as possible. Interlocked.Increment(ref this.activeConnectionCount); if (Environment.Is64BitProcess || MemoryHelper.IsMemoryAvailable()) { CompilerServerLogger.Log("Memory available - accepting connection"); Connection connection = new Connection(pipeStream, handler); await connection.ServeConnection().ConfigureAwait(false); // The connection should be finished ConnectionCompleted(connection); } else { CompilerServerLogger.Log("Memory tight - rejecting connection."); // As long as we haven't written a response, the client has not // committed to this server instance and can look elsewhere. pipeStream.Close(); // We didn't create a connection -- decrement the semaphore Interlocked.Decrement(ref this.activeConnectionCount); // Start a terminate server timer if there are no active // connections StartTimeoutTimerIfNecessary(); } } catch (Exception e) if (CompilerFatalError.Report(e)) { throw ExceptionUtilities.Unreachable; } } /// <summary> /// Create an instance of the pipe. This might be the first instance, or a subsequent instance. /// There always needs to be an instance of the pipe created to listen for a new client connection. /// </summary> /// <returns>The pipe instance, or NULL if the pipe couldn't be created..</returns> private NamedPipeServerStream ConstructPipe() { // Add the process ID onto the pipe name so each process gets a unique pipe name. // The client must user this algorithm too to connect. string pipeName = basePipeName + Process.GetCurrentProcess().Id.ToString(); try { CompilerServerLogger.Log("Constructing pipe '{0}'.", pipeName); SecurityIdentifier identifier = WindowsIdentity.GetCurrent().Owner; PipeSecurity security = new PipeSecurity(); // Restrict access to just this account. PipeAccessRule rule = new PipeAccessRule(identifier, PipeAccessRights.ReadWrite | PipeAccessRights.CreateNewInstance, AccessControlType.Allow); security.AddAccessRule(rule); security.SetOwner(identifier); NamedPipeServerStream pipeStream = new NamedPipeServerStream( pipeName, PipeDirection.InOut, NamedPipeServerStream.MaxAllowedServerInstances, // Maximum connections. PipeTransmissionMode.Byte, PipeOptions.Asynchronous | PipeOptions.WriteThrough, PipeBufferSize, // Default input buffer PipeBufferSize, // Default output buffer security, HandleInheritability.None); CompilerServerLogger.Log("Successfully constructed pipe '{0}'.", pipeName); return pipeStream; } catch (Exception e) { // Windows may not create the pipe for a number of reasons. CompilerServerLogger.LogException(e, string.Format("Construction of pipe '{0}' failed", pipeName)); return null; } }
/// <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); }