internal static Task CreateWatchFilesTask() { var source = new TaskCompletionSource <bool>(); var _ = new AnalyzerWatcher(source); return(source.Task); }
/// <summary> /// Create a new server that listens on the given base pipe name. /// When a request comes in, it is dispatched on a separate thread /// via the IRequestHandler interface passed in. /// </summary> /// <param name="basePipeName">Base name for named pipe</param> /// <param name="handler">Handler that handles requests</param> /// <param name="serverDieTimeout"> /// The timeout in milliseconds before the server automatically dies. /// </param> public ServerDispatcher(string basePipeName, IRequestHandler handler, int serverDieTimeout) { this.basePipeName = basePipeName; this.handler = handler; this.keepAliveTimer = new KeepAliveTimer(serverDieTimeout); var _ = new AnalyzerWatcher(this); }
public void ListenAndDispatchConnections(string pipeName, TimeSpan?keepAlive, bool watchAnalyzerFiles, CancellationToken cancellationToken = default(CancellationToken)) { Debug.Assert(SynchronizationContext.Current == null); var isKeepAliveDefault = true; var connectionList = new List <ConnectionData>(); Task gcTask = null; Task timeoutTask = null; Task <NamedPipeServerStream> listenTask = null; CancellationTokenSource listenCancellationTokenSource = null; // If we aren't being asked to watch analyzer files then simple create a Task which never // completes. This is the behavior of AnalyzerWatcher when files don't change on disk. Task analyzerTask = watchAnalyzerFiles ? AnalyzerWatcher.CreateWatchFilesTask() : new TaskCompletionSource <bool>().Task; 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 = CreateListenTask(pipeName, 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, analyzerTask }, cancellationToken); // If there is a connection event that has highest priority. if (listenTask.IsCompleted && !cancellationToken.IsCancellationRequested) { var changeKeepAliveSource = new TaskCompletionSource <TimeSpan?>(); var connectionTask = CreateHandleConnectionTask(listenTask, changeKeepAliveSource, cancellationToken); connectionList.Add(new ConnectionData(connectionTask, changeKeepAliveSource.Task)); listenTask = null; listenCancellationTokenSource = null; timeoutTask = null; gcTask = null; continue; } if ((timeoutTask != null && timeoutTask.IsCompleted) || analyzerTask.IsCompleted || cancellationToken.IsCancellationRequested) { 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. listenCancellationTokenSource.Cancel(); break; } if (connectionList.Count == 0 && gcTask == null) { gcTask = Task.Delay(s_GCTimeout); } } while (true); try { Task.WaitAll(connectionList.Select(x => x.ConnectionTask).ToArray()); } 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. } }
/// <summary> /// Create a new server that listens on the given base pipe name. /// When a request comes in, it is dispatched on a separate thread /// via the IRequestHandler interface passed in. /// </summary> /// <param name="basePipeName">Base name for named pipe</param> /// <param name="handler">Handler that handles requests</param> /// <param name="serverDieTimeout"> /// The timeout in milliseconds before the server automatically dies. /// </param> public ServerDispatcher(string basePipeName, IRequestHandler handler, int serverDieTimeout) { this.basePipeName = basePipeName; this.handler = handler; this.serverDieTimeout = serverDieTimeout; var _ = new AnalyzerWatcher(this); }
internal static Task CreateWatchFilesTask() { var source = new TaskCompletionSource<bool>(); var _ = new AnalyzerWatcher(source); return source.Task; }