public static int RunCompiler( string clientDirectory, string[] args, string baseDirectory, string sdkDirectory, string libDirectory, IAnalyzerAssemblyLoader analyzerLoader, TextWriter output, CancellationToken cancellationToken, out bool utf8output) { var compiler = new CSharpCompilerServer(args, clientDirectory, baseDirectory, sdkDirectory, libDirectory, analyzerLoader); utf8output = compiler.Arguments.Utf8Output; foreach (var analyzer in compiler.Arguments.AnalyzerReferences) { CompilerServerFileWatcher.AddPath(analyzer.FilePath); } return(compiler.Run(output, cancellationToken)); }
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 ? CompilerServerFileWatcher.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. } }