private void HandleCompletedTimeoutTask() { _diagnosticListener.KeepAliveReached(); _listenCancellationTokenSource.Cancel(); _timeoutTask = null; _state = State.ShuttingDown; }
private void HandleCompletedTimeoutTask() { CompilerServerLogger.Log("Timeout triggered. Shutting down server."); _diagnosticListener.KeepAliveReached(); _listenCancellationTokenSource.Cancel(); _timeoutTask = null; _state = State.ShuttingDown; }
private void CheckCompletedTasks(CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { ChangeToShuttingDown("Server cancellation"); Debug.Assert(_gcTask is null); Debug.Assert(_timeoutTask is null); } if (_listenTask?.IsCompleted == true) { _diagnosticListener.ConnectionReceived(); var connectionTask = ProcessClientConnectionAsync( _compilerServerHost, _listenTask, allowCompilationRequests: _state == State.Running, cancellationToken ); _connectionList.Add(connectionTask); // Timeout and GC are only done when there are no active connections. Now that we have a new // connection cancel out these tasks. _timeoutTask = null; _gcTask = null; _listenTask = null; } if (_timeoutTask?.IsCompleted == true) { _diagnosticListener.KeepAliveReached(); ChangeToShuttingDown("Keep alive hit"); } if (_gcTask?.IsCompleted == true) { RunGC(); } HandleCompletedConnections(); }
/// <summary> /// This function will accept and process new connections until an event causes /// the server to enter a passive shut down mode. For example if analyzers change /// or the keep alive timeout is hit. At which point this function will cease /// accepting new connections and wait for existing connections to complete before /// returning. /// </summary> public void ListenAndDispatchConnections(TimeSpan?keepAlive, CancellationToken cancellationToken = default(CancellationToken)) { var isKeepAliveDefault = true; var connectionList = new List <Task <ConnectionData> >(); Task gcTask = null; Task timeoutTask = null; Task <IClientConnection> listenTask = null; CancellationTokenSource listenCancellationTokenSource = null; do { // While this loop is running there should be an active named pipe listening for a // connection. if (listenTask == null) { Debug.Assert(listenCancellationTokenSource == null); Debug.Assert(timeoutTask == null); listenCancellationTokenSource = new CancellationTokenSource(); listenTask = _clientConnectionHost.CreateListenTask(listenCancellationTokenSource.Token); } // If there are no active clients running then the server needs to be in a timeout mode. if (connectionList.Count == 0 && timeoutTask == null && keepAlive.HasValue) { Debug.Assert(listenTask != null); timeoutTask = Task.Delay(keepAlive.Value); } WaitForAnyCompletion(connectionList, new[] { listenTask, timeoutTask, gcTask }, cancellationToken); // If there is a connection event that has highest priority. if (listenTask.IsCompleted && !cancellationToken.IsCancellationRequested) { _diagnosticListener.ConnectionReceived(); var connectionTask = HandleClientConnection(listenTask, cancellationToken); connectionList.Add(connectionTask); listenTask = null; listenCancellationTokenSource = null; timeoutTask = null; gcTask = null; continue; } if ((timeoutTask != null && timeoutTask.IsCompleted) || cancellationToken.IsCancellationRequested) { _diagnosticListener.KeepAliveReached(); listenCancellationTokenSource.Cancel(); break; } if (gcTask != null && gcTask.IsCompleted) { gcTask = null; GC.Collect(); continue; } // Only other option is a connection event. Go ahead and clear out the dead connections if (!CheckConnectionTask(connectionList, ref keepAlive, ref isKeepAliveDefault)) { // If there is a client disconnection detected then the server needs to begin // the shutdown process. We have to assume that the client disconnected via // Ctrl+C and wants the server process to terminate. It's possible a compilation // is running out of control and the client wants their machine back. _diagnosticListener.ConnectionRudelyEnded(); listenCancellationTokenSource.Cancel(); break; } if (connectionList.Count == 0 && gcTask == null) { gcTask = Task.Delay(GCTimeout); } } while (true); try { if (connectionList.Count > 0) { Task.WaitAll(connectionList.ToArray()); TimeSpan?ignoredTimeSpan = null; bool ignoredBool = false; CheckConnectionTask(connectionList, ref ignoredTimeSpan, ref ignoredBool); } } catch { // Server is shutting down, don't care why the above failed and Exceptions // are expected here. For example AggregateException via, OperationCancelledException // is an expected case. } }