Esempio n. 1
0
        /// <summary>
        /// Checks the completed connection objects.
        /// </summary>
        /// <returns>False if the server needs to begin shutting down</returns>
        private void HandleCompletedConnections()
        {
            var shutdown       = false;
            var processedCount = 0;
            var i = 0;

            while (i < _connectionList.Count)
            {
                var current = _connectionList[i];
                if (!current.IsCompleted)
                {
                    i++;
                    continue;
                }

                _connectionList.RemoveAt(i);
                processedCount++;

                var connectionData = current.Result;
                ChangeKeepAlive(connectionData.KeepAlive);

                switch (connectionData.CompletionReason)
                {
                case CompletionReason.CompilationCompleted:
                case CompletionReason.CompilationNotStarted:
                    // These are all normal shutdown states.  Nothing to do here.
                    break;

                case CompletionReason.ClientDisconnect:
                    // Have to assume the worst here which is user pressing Ctrl+C at the command line and
                    // hence wanting all compilation to end.
                    _diagnosticListener.ConnectionRudelyEnded();
                    shutdown = true;
                    break;

                case CompletionReason.ClientException:
                case CompletionReason.ClientShutdownRequest:
                    _diagnosticListener.ConnectionRudelyEnded();
                    shutdown = true;
                    break;

                default:
                    throw new InvalidOperationException($"Unexpected enum value {connectionData.CompletionReason}");
                }
            }

            if (processedCount > 0)
            {
                _diagnosticListener.ConnectionCompleted(processedCount);
            }

            if (shutdown)
            {
                _state = State.ShuttingDown;
            }
        }
Esempio n. 2
0
        /// <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.
            }
        }