/// <summary>
        /// An incoming request as occurred. This is called on a new thread to handle
        /// the request.
        /// </summary>
        public BuildResponse HandleRequest(BuildRequest req, CancellationToken cancellationToken)
        {
            switch (req.Language)
            {
                case BuildProtocolConstants.RequestLanguage.CSharpCompile:
                    CompilerServerLogger.Log("Request to compile C#");
                    return CSharpCompile(req, cancellationToken);

                case BuildProtocolConstants.RequestLanguage.VisualBasicCompile:
                    CompilerServerLogger.Log("Request to compile VB");
                    return BasicCompile(req, cancellationToken);

                default:
                    CompilerServerLogger.Log("Got request with id '{0}'", req.Language);
                    for (int i = 0; i < req.Arguments.Length; ++i)
                    {
                        CompilerServerLogger.Log("Request argument '{0}[{1}]' = '{2}'", req.Arguments[i].ArgumentId, req.Arguments[i].ArgumentIndex, req.Arguments[i].Value);
                    }

                    // We can't do anything with a request we don't know about. 
                    return new CompletedBuildResponse(-1,
                        utf8output: false,
                        output: "",
                        errorOutput: "");
            }
        }
Beispiel #2
0
        public async Task<BuildResponse> GetResponseAsync(BuildRequest req, 
                                                     CancellationToken cancellationToken)
        {
            NamedPipeClientStream pipeStream;
            if (TryAutoConnectToServer(cancellationToken, out pipeStream))
            {
                // We have a good connection
                BuildResponse response = await DoCompilationAsync(pipeStream, req, cancellationToken).ConfigureAwait(false);
                if (response != null)
                {
                    return response;
                }
                else
                {
                    CompilerServerLogger.Log("Compilation failed, constructing new compiler server");
                    // The compilation failed. There are a couple possible reasons for this,
                    // including that we are using a 32-bit compiler server and we are out of 
                    // memory. This is the last attempt -- we will create a new server manually
                    // and try to compile. There is no mutex because anyone else using
                    // this server is accidental only.
                    int newProcessId = CreateNewServerProcess();
                    if (newProcessId != 0 &&
                        TryConnectToProcess(newProcessId,
                                            TimeOutMsNewProcess,
                                            cancellationToken,
                                            out pipeStream))
                    {
                        return await DoCompilationAsync(pipeStream, req, cancellationToken).ConfigureAwait(false);
                    }
                }
            }

            return null;
        }
        private static string[] GetCommandLineArguments(BuildRequest req, out string currentDirectory, out string libDirectory)
        {
            currentDirectory = null;
            libDirectory = null;
            List<string> commandLineArguments = new List<string>();

            foreach (BuildRequest.Argument arg in req.Arguments)
            {
                if (arg.ArgumentId == BuildProtocolConstants.ArgumentId_CurrentDirectory)
                {
                    currentDirectory = arg.Value;
                }
                else if (arg.ArgumentId == BuildProtocolConstants.ArgumentId_LibEnvVariable)
                {
                    libDirectory = arg.Value;
                }
                else if (arg.ArgumentId == BuildProtocolConstants.ArgumentId_CommandLineArgument)
                {
                    uint argIndex = arg.ArgumentIndex;
                    while (argIndex >= commandLineArguments.Count)
                        commandLineArguments.Add("");
                    commandLineArguments[(int)argIndex] = arg.Value;
                }
            }

            return commandLineArguments.ToArray();
        }
        /// <summary>
        /// An incoming request as occurred. This is called on a new thread to handle
        /// the request.
        /// </summary>
        public BuildResponse HandleRequest(BuildRequest req, CancellationToken cancellationToken)
        {
            switch (req.Id)
            {
                case BuildProtocolConstants.RequestId_CSharpCompile:
                    CompilerServerLogger.Log("Request to compile C#");
                    return CSharpCompile(req, cancellationToken);

                case BuildProtocolConstants.RequestId_VisualBasicCompile:
                    CompilerServerLogger.Log("Request to compile VB");
                    return BasicCompile(req, cancellationToken);

                case BuildProtocolConstants.RequestId_Analyze:
                    CompilerServerLogger.Log("Request to analyze managed code");
                    return Analyze(req, cancellationToken);

                default:
                    CompilerServerLogger.Log("Got request with id '{0}'", req.Id);
                    for (int i = 0; i < req.Arguments.Length; ++i)
                    {
                        CompilerServerLogger.Log("Request argument '{0}[{1}]' = '{2}'", req.Arguments[i].ArgumentId, req.Arguments[i].ArgumentIndex, req.Arguments[i].Value);
                    }

                    // We can't do anything with a request we don't know about. 
                    return new BuildResponse(0, "", "");
            }
        }
        internal static int ExecuteTool(BuildRequest request, CancellationToken cancellationToken, out bool canceled, out string output, out string errorOutput)
        {
            canceled = false;

            BuildClient client = new BuildClient();
            Task<BuildResponse> responseTask = client.GetResponseAsync(request, cancellationToken);

            BuildResponse response = null;
            try
            {
                responseTask.Wait(cancellationToken);
                response = responseTask.Result;
            }
            catch (OperationCanceledException)
            {
                canceled = true;
                output = null;
                errorOutput = null;
                return 0;
            }
            catch (AggregateException ae)
            {
                CompilerServerLogger.LogException(ae, "Unexpected failure.");
                foreach (var e in ae.InnerExceptions)
                {
                    CompilerServerLogger.LogException(e, "");
                }
            }

            if (response == null)
            {
                output = null;
                errorOutput = "Fatal Error: Please see the event log for more details.";
                return -1;
            }

            output = response.Output;
            errorOutput = response.ErrorOutput;
            return response.ReturnCode;
        }
Beispiel #6
0
        public async Task <ConnectionData> HandleConnection(bool allowCompilationRequests = true, CancellationToken cancellationToken = default(CancellationToken))
        {
            try
            {
                BuildRequest request;
                try
                {
                    Log("Begin reading request.");
                    request = await BuildRequest.ReadAsync(_stream, cancellationToken).ConfigureAwait(false);

                    ValidateBuildRequest(request);
                    Log("End reading request.");
                }
                catch (Exception e)
                {
                    LogException(e, "Error reading build request.");
                    return(new ConnectionData(CompletionReason.CompilationNotStarted));
                }

                if (IsShutdownRequest(request))
                {
                    return(await HandleShutdownRequest(cancellationToken).ConfigureAwait(false));
                }
                else if (!allowCompilationRequests)
                {
                    return(await HandleRejectedRequest(cancellationToken).ConfigureAwait(false));
                }
                else
                {
                    return(await HandleCompilationRequest(request, cancellationToken).ConfigureAwait(false));
                }
            }
            finally
            {
                Close();
            }
        }
Beispiel #7
0
        internal static string[] GetCommandLineArguments(BuildRequest req, out string?currentDirectory, out string?tempDirectory, out string?libDirectory)
        {
            currentDirectory = null;
            libDirectory     = null;
            tempDirectory    = null;
            List <string> commandLineArguments = new List <string>();

            foreach (BuildRequest.Argument arg in req.Arguments)
            {
                if (arg.ArgumentId == BuildProtocolConstants.ArgumentId.CurrentDirectory)
                {
                    currentDirectory = arg.Value;
                }
                else if (arg.ArgumentId == BuildProtocolConstants.ArgumentId.TempDirectory)
                {
                    tempDirectory = arg.Value;
                }
                else if (arg.ArgumentId == BuildProtocolConstants.ArgumentId.LibEnvVariable)
                {
                    libDirectory = arg.Value;
                }
                else if (arg.ArgumentId == BuildProtocolConstants.ArgumentId.CommandLineArgument)
                {
                    if (arg.Value is object)
                    {
                        int argIndex = arg.ArgumentIndex;
                        while (argIndex >= commandLineArguments.Count)
                        {
                            commandLineArguments.Add("");
                        }
                        commandLineArguments[argIndex] = arg.Value;
                    }
                }
            }

            return(commandLineArguments.ToArray());
        }
        /// <summary>
        /// A request to compile VB files. Unpack the arguments and current directory and invoke
        /// the compiler, then create a response with the result of compilation.
        /// </summary>
        private BuildResponse BasicCompile(BuildRequest req, CancellationToken cancellationToken)
        {
            string currentDirectory;
            string libDirectory;
            string tempPath;
            var    commandLineArguments = GetCommandLineArguments(req, out currentDirectory, out libDirectory, out tempPath);

            if (currentDirectory == null)
            {
                // If we don't have a current directory, compilation can't proceed. This shouldn't ever happen,
                // because our clients always send the current directory.
                Debug.Assert(false, "Client did not send current directory; this is required.");
                return(new CompletedBuildResponse(-1, utf8output: false, output: "", errorOutput: ""));
            }

            if (tempPath == null)
            {
                // If we don't have a temp directory, compilation can't proceed. This shouldn't ever happen,
                // because our clients always send the temp directory.
                Debug.Assert(false, "Client did not send temp directory; this is required.");
                return(new CompletedBuildResponse(-1, utf8output: false, output: "", errorOutput: ""));
            }

            TextWriter output = new StringWriter(CultureInfo.InvariantCulture);
            bool       utf8output;
            int        returnCode = BasicCompile(
                _responseFileDirectory,
                currentDirectory,
                libDirectory,
                tempPath,
                commandLineArguments,
                output,
                cancellationToken,
                out utf8output);

            return(new CompletedBuildResponse(returnCode, utf8output, output.ToString(), ""));
        }
Beispiel #9
0
        private BuildResponse ServeBuildRequestCore(BuildRequest buildRequest, CancellationToken cancellationToken)
        {
            var            request = BuildProtocolUtil.GetRunRequest(buildRequest);
            CommonCompiler compiler;

            if (!_compilerServerHost.TryCreateCompiler(request, out compiler))
            {
                // TODO: is this the right option?  Right now it will cause the fall back in the command line
                // to fail because it's a valid response.  Probably should add a "server failed" response
                // for command line to deal with.

                // We can't do anything with a request we don't know about.
                Log($"Got request with id '{request.Language}'");
                return(new CompletedBuildResponse(-1, false, "", ""));
            }

            Log($"CurrentDirectory = '{request.CurrentDirectory}'");
            Log($"LIB = '{request.LibDirectory}'");
            for (int i = 0; i < request.Arguments.Length; ++i)
            {
                Log($"Argument[{i}] = '{request.Arguments[i]}'");
            }

            bool utf8output = compiler.Arguments.Utf8Output;

            if (!_compilerServerHost.CheckAnalyzers(request.CurrentDirectory, compiler.Arguments.AnalyzerReferences))
            {
                return(new AnalyzerInconsistencyBuildResponse());
            }

            Log($"****Running {request.Language} compiler...");
            TextWriter output     = new StringWriter(CultureInfo.InvariantCulture);
            int        returnCode = compiler.Run(output, cancellationToken);

            Log($"****{request.Language} Compilation complete.\r\n****Return code: {returnCode}\r\n****Output:\r\n{output.ToString()}\r\n");
            return(new CompletedBuildResponse(returnCode, utf8output, output.ToString(), ""));
        }
        /// <summary>
        /// A request to compile C# files. Unpack the arguments and current directory and invoke
        /// the compiler, then create a response with the result of compilation.
        /// </summary>
        private BuildResponse CSharpCompile(BuildRequest req, CancellationToken cancellationToken)
        {
            string currentDirectory;
            string libDirectory;
            var    commandLineArguments = GetCommandLineArguments(req, out currentDirectory, out libDirectory);

            if (currentDirectory == null)
            {
                // If we don't have a current directory, compilation can't proceed. This shouldn't ever happen,
                // because our clients always send the current directory.
                Debug.Assert(false, "Client did not send current directory; this is required.");
                return(new CompletedBuildResponse(-1,
                                                  utf8output: false,
                                                  output: "",
                                                  errorOutput: ""));
            }

            return(CSharpCompile(
                       currentDirectory,
                       libDirectory,
                       _responseFileDirectory,
                       commandLineArguments,
                       cancellationToken));
        }
Beispiel #11
0
            public async Task ServeConnection()
            {
                BuildRequest request;

                try
                {
                    Log("Begin reading request");
                    request = await BuildRequest.ReadAsync(pipeStream, cancellationTokenSource.Token).ConfigureAwait(false);

                    Log("End reading request");
                }
                catch (IOException e)
                {
                    LogException(e, "Reading request from named pipe.");
                    FinishConnection(CompletionReason.IOFailure);
                    return;
                }
                catch (OperationCanceledException e)
                {
                    LogException(e, "Reading request from named pipe.");
                    FinishConnection(CompletionReason.Cancelled);
                    return;
                }

                if (!ClientAndOurIdentitiesMatch(pipeStream))
                {
                    Log("Client identity doesn't match.");
                    FinishConnection(CompletionReason.SecurityViolation);
                    return;
                }

                CheckForNewKeepAlive(request);

                // Start a monitor that cancels if the pipe closes on us
                var _ = MonitorPipeForDisconnection().ConfigureAwait(false);

                // Do the compilation
                Log("Begin compilation");
                BuildResponse response = await Task.Run(() =>
                {
                    try
                    {
                        return(handler.HandleRequest(request, cancellationTokenSource.Token));
                    }
                    catch (Exception e) if (CompilerFatalError.ReportUnlessCanceled(e))
                        {
                            throw ExceptionUtilities.Unreachable;
                        }
                }).ConfigureAwait(false);

                Log("End compilation");

                try
                {
                    Log("Begin writing response");
                    await response.WriteAsync(pipeStream, cancellationTokenSource.Token).ConfigureAwait(false);

                    Log("End writing response");
                }
                catch (IOException e)
                {
                    LogException(e, "Writing response to named pipe.");
                    FinishConnection(CompletionReason.IOFailure);
                    return;
                }
                catch (OperationCanceledException e)
                {
                    LogException(e, "Writing response to named pipe.");
                    FinishConnection(CompletionReason.Cancelled);
                    return;
                }

                Log("Completed writing response to named pipe.");
                FinishConnection(CompletionReason.Success);
            }
Beispiel #12
0
        /// <summary>
        /// Try to compile using the server. Returns null if a response from the
        /// server cannot be retrieved.
        /// </summary>
        private static async Task<BuildResponse> TryCompile(NamedPipeClientStream pipeStream,
                                                            BuildRequest request,
                                                            CancellationToken cancellationToken)
        {
            BuildResponse response;
            using (pipeStream)
            {
                // Write the request
                try
                {
                    Log("Begin writing request");
                    await request.WriteAsync(pipeStream, cancellationToken).ConfigureAwait(false);
                    Log("End writing request");
                }
                catch (Exception e)
                {
                    LogException(e, "Error writing build request.");
                    return null;
                }

                // Wait for the compilation and a monitor to detect if the server disconnects
                var serverCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);

                Log("Begin reading response");

                var responseTask = BuildResponse.ReadAsync(pipeStream, serverCts.Token);
                var monitorTask = CreateMonitorDisconnectTask(pipeStream, serverCts.Token);
                await Task.WhenAny(responseTask, monitorTask).ConfigureAwait(false);

                Log("End reading response");

                if (responseTask.IsCompleted)
                {
                    // await the task to log any exceptions
                    try
                    {
                        response = await responseTask.ConfigureAwait(false);
                    }
                    catch (Exception e)
                    {
                        LogException(e, "Error reading response");
                        response = null;
                    }
                }
                else
                {
                    Log("Server disconnect");
                    response = null;
                }

                // Cancel whatever task is still around
                serverCts.Cancel();
                return response;
            }
        }
        private async Task <CompletionData> ProcessCompilationRequestAsync(IClientConnection clientConnection, BuildRequest request, CancellationToken cancellationToken)
        {
            // Need to wait for the compilation and client disconnection in parallel. If the client
            // suddenly disconnects we need to cancel the compilation that is occuring. It could be the
            // client hit Ctrl-C due to a run away analyzer.
            var buildCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
            var compilationTask = ProcessCompilationRequestCore(CompilerServerHost, request, buildCancellationTokenSource.Token);
            await Task.WhenAny(compilationTask, clientConnection.DisconnectTask).ConfigureAwait(false);

            try
            {
                if (compilationTask.IsCompleted)
                {
                    BuildResponse response;
                    try
                    {
                        response = await compilationTask.ConfigureAwait(false);
                    }
                    catch (Exception ex)
                    {
                        CompilerServerLogger.LogException(ex, $"Exception running compilation for {clientConnection.LoggingIdentifier}");
                        response = new RejectedBuildResponse($"Exception during compilation: {ex.Message}");
                    }

                    await clientConnection.WriteBuildResponseAsync(response, cancellationToken).ConfigureAwait(false);

                    var newKeepAlive     = CheckForNewKeepAlive(request);
                    var completionReason = response switch
                    {
                        AnalyzerInconsistencyBuildResponse _ => CompletionReason.RequestError,
                        RejectedBuildResponse _ => CompletionReason.RequestError,
                        _ => CompletionReason.RequestCompleted
                    };
                    return(new CompletionData(completionReason, newKeepAlive));
                }
                else
                {
                    return(CompletionData.RequestError);
                }
            }
            finally
            {
                buildCancellationTokenSource.Cancel();
            }
            static Task <BuildResponse> ProcessCompilationRequestCore(ICompilerServerHost compilerServerHost, BuildRequest buildRequest, CancellationToken cancellationToken)
            {
                Func <BuildResponse> func = () =>
                {
                    var request  = BuildProtocolUtil.GetRunRequest(buildRequest);
                    var response = compilerServerHost.RunCompilation(request, cancellationToken);
                    return(response);
                };

                var task = new Task <BuildResponse>(func, cancellationToken, TaskCreationOptions.LongRunning);

                task.Start();
                return(task);
            }
Beispiel #15
0
        private async Task <CompletionData> ProcessCompilationRequestAsync(IClientConnection clientConnection, BuildRequest request, CancellationToken cancellationToken)
        {
            // Need to wait for the compilation and client disconnection in parallel. If the client
            // suddenly disconnects we need to cancel the compilation that is occuring. It could be the
            // client hit Ctrl-C due to a run away analyzer.
            var buildCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
            var compilationTask = ProcessCompilationRequestCore(CompilerServerHost, request, buildCancellationTokenSource.Token);
            await Task.WhenAny(compilationTask, clientConnection.DisconnectTask).ConfigureAwait(false);

            try
            {
                if (compilationTask.IsCompleted)
                {
                    BuildResponse  response;
                    CompletionData completionData;
                    try
                    {
                        response = await compilationTask.ConfigureAwait(false);

                        completionData = response switch
                        {
                            // Once there is an analyzer inconsistency the assembly load space is polluted. The
                            // request is an error.
                            AnalyzerInconsistencyBuildResponse _ => CompletionData.RequestError,
                                                               _ => new CompletionData(CompletionReason.RequestCompleted, newKeepAlive: CheckForNewKeepAlive(request))
                        };
                    }
                    catch (Exception ex)
                    {
                        // The compilation task should never throw. If it does we need to assume that the compiler is
                        // in a bad state and need to issue a RequestError
                        CompilerServerLogger.LogException(ex, $"Exception running compilation for {clientConnection.LoggingIdentifier}");
                        response       = new RejectedBuildResponse($"Exception during compilation: {ex.Message}");
                        completionData = CompletionData.RequestError;
                    }

                    return(await WriteBuildResponseAsync(
                               clientConnection,
                               response,
                               completionData,
                               cancellationToken).ConfigureAwait(false));
                }
                else
                {
                    return(CompletionData.RequestError);
                }
            }
            finally
            {
                buildCancellationTokenSource.Cancel();
            }
 private Task<BuildResponse> ServeBuildRequest(BuildRequest request, CancellationToken cancellationToken)
 {
     return Task.Run(() =>
     {
         try
         {
             // Do the compilation
             Log("Begin compilation");
             BuildResponse response = _handler.HandleRequest(request, cancellationToken);
             Log("End compilation");
             return response;
         }
         catch (Exception e) when (FatalError.Report(e))
         {
             throw ExceptionUtilities.Unreachable;
         }
     });
 }
Beispiel #17
0
        /// <summary>
        /// Shutting down the server is an inherently racy operation.  The server can be started or stopped by
        /// external parties at any time.
        ///
        /// This function will return success if at any time in the function the server is determined to no longer
        /// be running.
        /// </summary>
        internal async Task <int> RunShutdownAsync(
            string pipeName,
            bool waitForProcess = true,
            TimeSpan?timeout    = null,
            CancellationToken cancellationToken = default
            )
        {
            if (WasServerRunning(pipeName) == false)
            {
                // The server holds the mutex whenever it is running, if it's not open then the
                // server simply isn't running.
                return(CommonCompiler.Succeeded);
            }

            try
            {
                var realTimeout =
                    timeout != null ? (int)timeout.Value.TotalMilliseconds : Timeout.Infinite;
                using var client = await ConnectForShutdownAsync(pipeName, realTimeout)
                                   .ConfigureAwait(false);

                if (client is object)
                {
                    var request = BuildRequest.CreateShutdown();
                    await request.WriteAsync(client, cancellationToken).ConfigureAwait(false);

                    var response = await BuildResponse
                                   .ReadAsync(client, cancellationToken)
                                   .ConfigureAwait(false);

                    var shutdownResponse = (ShutdownBuildResponse)response;

                    if (waitForProcess)
                    {
                        try
                        {
                            var process = Process.GetProcessById(shutdownResponse.ServerProcessId);
                            process.WaitForExit();
                        }
                        catch (Exception)
                        {
                            // There is an inherent race here with the server process.  If it has already shutdown
                            // by the time we try to access it then the operation has succeed.
                        }
                    }
                }

                return(CommonCompiler.Succeeded);
            }
            catch (Exception)
            {
                if (WasServerRunning(pipeName) == false)
                {
                    // If the server was in the process of shutting down when we connected then it's reasonable
                    // for an exception to happen.  If the mutex has shutdown at this point then the server
                    // is shut down.
                    return(CommonCompiler.Succeeded);
                }

                return(CommonCompiler.Failed);
            }
        }
        /// <summary>
        /// A request to analyze managed source code files. Unpack the arguments and current directory and invoke
        /// the analyzer, then create a response with the result of compilation.
        /// </summary>
        private BuildResponse Analyze(BuildRequest req, CancellationToken cancellationToken)
        {
            string currentDirectory;
            string libDirectory;
            var commandLineArguments = GetCommandLineArguments(req, out currentDirectory, out libDirectory);

            // Server based execution of Roslyn Diagnostic Providers (Disabled for now).
            throw new NotImplementedException("Server based execution of Roslyn Diagnostic Providers NYI.");

            // TextWriter output = new StringWriter(CultureInfo.InvariantCulture);
            // var task = Microsoft.CodeAnalysis.Diagnostics.CommandLineDiagnosticService.ComputeAndWriteDiagnosticsAsync(commandLineArguments, output, cancellationToken);
            // task.Wait(cancellationToken);
            // int returnCode = task.Result;
            // return new BuildResponse(returnCode, "", output.ToString());
        }
Beispiel #19
0
 protected virtual void ValidateBuildRequest(BuildRequest request)
 {
 }
Beispiel #20
0
        /// <summary>
        /// A request to compile VB files. Unpack the arguments and current directory and invoke
        /// the compiler, then create a response with the result of compilation.
        /// </summary>
        private BuildResponse BasicCompile(BuildRequest req, CancellationToken cancellationToken)
        {
            string currentDirectory;
            string libDirectory;
            var commandLineArguments = GetCommandLineArguments(req, out currentDirectory, out libDirectory);

            if (currentDirectory == null)
            {
                // If we don't have a current directory, compilation can't proceed. This shouldn't ever happen,
                // because our clients always send the current directory.
                Debug.Assert(false, "Client did not send current directory; this is required.");
                return new CompletedBuildResponse(-1, utf8output: false, output: "", errorOutput: "");
            }

            return BasicCompile(
                _responseFileDirectory,
                currentDirectory,
                libDirectory,
                commandLineArguments,
                cancellationToken);
        }
Beispiel #21
0
        public async Task <ConnectionData> HandleConnection(CancellationToken cancellationToken)
        {
            try
            {
                BuildRequest request;
                try
                {
                    Log("Begin reading request.");
                    request = await BuildRequest.ReadAsync(_stream, cancellationToken).ConfigureAwait(false);

                    ValidateBuildRequest(request);
                    Log("End reading request.");
                }
                catch (Exception e)
                {
                    LogException(e, "Error reading build request.");
                    return(new ConnectionData(CompletionReason.CompilationNotStarted));
                }

                var keepAlive = CheckForNewKeepAlive(request);

                // Kick off both the compilation and a task to monitor the pipe for closing.
                var buildCts        = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
                var compilationTask = ServeBuildRequest(request, buildCts.Token);
                var monitorTask     = CreateMonitorDisconnectTask(buildCts.Token);
                await Task.WhenAny(compilationTask, monitorTask).ConfigureAwait(false);

                // Do an 'await' on the completed task, preference being compilation, to force
                // any exceptions to be realized in this method for logging.
                CompletionReason reason;
                if (compilationTask.IsCompleted)
                {
                    var response = await compilationTask.ConfigureAwait(false);

                    try
                    {
                        Log("Begin writing response.");
                        await response.WriteAsync(_stream, cancellationToken).ConfigureAwait(false);

                        reason = CompletionReason.Completed;
                        Log("End writing response.");
                    }
                    catch
                    {
                        reason = CompletionReason.ClientDisconnect;
                    }
                }
                else
                {
                    await monitorTask.ConfigureAwait(false);

                    reason = CompletionReason.ClientDisconnect;
                }

                // Begin the tear down of the Task which didn't complete.
                buildCts.Cancel();
                return(new ConnectionData(reason, keepAlive));
            }
            finally
            {
                Close();
            }
        }
Beispiel #22
0
        private async Task<BuildResponse> DoCompilationAsync(NamedPipeClientStream pipeStream,
                                                        BuildRequest req,
                                                        CancellationToken cancellationToken)
        {
            using (pipeStream)
            {
                try
                {
                    // Start a monitor that cancels if the pipe closes on us
                    var monitorCancellation = new CancellationTokenSource();
                    Task disconnectMonitor = MonitorPipeForDisconnectionAsync(pipeStream, monitorCancellation.Token);

                    // Write the request.
                    CompilerServerLogger.Log("Writing request");
                    await req.WriteAsync(pipeStream, cancellationToken).ConfigureAwait(false);

                    // Read the response.
                    CompilerServerLogger.Log("Reading response");
                    BuildResponse response = await BuildResponse.ReadAsync(pipeStream, cancellationToken).ConfigureAwait(false);

                    // Stop monitoring pipe
                    monitorCancellation.Cancel(throwOnFirstException: true);
                    await disconnectMonitor.ConfigureAwait(false);

                    Debug.Assert(response != null);
                    CompilerServerLogger.Log("BuildResponse received; exit code={0}", response.ReturnCode);

                    return response;
                }
                catch (PipeBrokenException e)
                {
                    CompilerServerLogger.LogException(e, "Server process died; pipe broken.");
                    return null;
                }
                catch (ObjectDisposedException e)
                {
                    CompilerServerLogger.LogException(e, "Pipe stream unexpectedly disposed");
                    return null;
                }
            }
        }
Beispiel #23
0
        /// <summary>
        /// Shutting down the server is an inherently racy operation.  The server can be started or stopped by
        /// external parties at any time.
        ///
        /// This function will return success if at any time in the function the server is determined to no longer
        /// be running.
        /// </summary>
        internal static async Task <int> RunShutdownAsync(string pipeName, bool waitForProcess = true, TimeSpan?timeout = null, CancellationToken cancellationToken = default(CancellationToken))
        {
            if (string.IsNullOrEmpty(pipeName))
            {
                var clientDirectory = AppDomain.CurrentDomain.BaseDirectory;
                pipeName = DesktopBuildClient.GetPipeNameFromFileInfo(clientDirectory);
            }

            var mutexName = BuildProtocolConstants.GetServerMutexName(pipeName);

            if (!DesktopBuildClient.WasServerMutexOpen(mutexName))
            {
                // The server holds the mutex whenever it is running, if it's not open then the
                // server simply isn't running.
                return(CommonCompiler.Succeeded);
            }

            try
            {
                using (var client = new NamedPipeClientStream(pipeName))
                {
                    var realTimeout = timeout != null
                        ? (int)timeout.Value.TotalMilliseconds
                        : Timeout.Infinite;
                    client.Connect(realTimeout);

                    var request = BuildRequest.CreateShutdown();
                    await request.WriteAsync(client, cancellationToken).ConfigureAwait(false);

                    var response = await BuildResponse.ReadAsync(client, cancellationToken).ConfigureAwait(false);

                    var shutdownResponse = (ShutdownBuildResponse)response;

                    if (waitForProcess)
                    {
                        try
                        {
                            var process = Process.GetProcessById(shutdownResponse.ServerProcessId);
                            process.WaitForExit();
                        }
                        catch (Exception)
                        {
                            // There is an inherent race here with the server process.  If it has already shutdown
                            // by the time we try to access it then the operation has succeed.
                        }
                    }
                }

                return(CommonCompiler.Succeeded);
            }
            catch (Exception)
            {
                if (!DesktopBuildClient.WasServerMutexOpen(mutexName))
                {
                    // If the server was in the process of shutting down when we connected then it's reasonable
                    // for an exception to happen.  If the mutex has shutdown at this point then the server
                    // is shut down.
                    return(CommonCompiler.Succeeded);
                }

                return(CommonCompiler.Failed);
            }
        }
        /// <summary>
        /// A request to compile C# files. Unpack the arguments and current directory and invoke
        /// the compiler, then create a response with the result of compilation.
        /// </summary>
        private BuildResponse CSharpCompile(BuildRequest req, CancellationToken cancellationToken)
        {
            string currentDirectory;
            string libDirectory;
            var commandLineArguments = GetCommandLineArguments(req, out currentDirectory, out libDirectory);

            if (currentDirectory == null)
            {
                // If we don't have a current directory, compilation can't proceed. This shouldn't ever happen,
                // because our clients always send the current directory.
                Debug.Assert(false, "Client did not send current directory; this is required.");
                return new BuildResponse(-1, "", "");
            }

            TextWriter output = new StringWriter(CultureInfo.InvariantCulture);
            int returnCode = CSharpCompile(currentDirectory, libDirectory, commandLineArguments, output, cancellationToken);

            return new BuildResponse(returnCode, output.ToString(), "");
        }
            /// <summary>
            /// Check the request arguments for a new keep alive time. If one is present,
            /// set the server timer to the new time.
            /// </summary>
            private void CheckForNewKeepAlive(BuildRequest request, TaskCompletionSource<TimeSpan?> timeoutCompletionSource)
            {
                TimeSpan? timeout = null;
                foreach (var arg in request.Arguments)
                {
                    if (arg.ArgumentId == BuildProtocolConstants.ArgumentId.KeepAlive)
                    {
                        int result;
                        // If the value is not a valid integer for any reason,
                        // ignore it and continue with the current timeout. The client
                        // is responsible for validating the argument.
                        if (int.TryParse(arg.Value, out result))
                        {
                            // Keep alive times are specified in seconds
                            timeout = TimeSpan.FromSeconds(result);
                        }
                    }
                }

                timeoutCompletionSource.SetResult(timeout);
            }
 private bool IsShutdownRequest(BuildRequest request)
 {
     return(request.Arguments.Count == 1 && request.Arguments[0].ArgumentId == BuildProtocolConstants.ArgumentId.Shutdown);
 }
        /// <summary>
        /// A request to compile VB files. Unpack the arguments and current directory and invoke
        /// the compiler, then create a response with the result of compilation.
        /// </summary>
        private BuildResponse BasicCompile(BuildRequest req, CancellationToken cancellationToken)
        {
            string currentDirectory;
            string libDirectory;
            string tempPath;
            var commandLineArguments = GetCommandLineArguments(req, out currentDirectory, out libDirectory, out tempPath);

            if (currentDirectory == null)
            {
                // If we don't have a current directory, compilation can't proceed. This shouldn't ever happen,
                // because our clients always send the current directory.
                Debug.Assert(false, "Client did not send current directory; this is required.");
                return new CompletedBuildResponse(-1, utf8output: false, output: "", errorOutput: "");
            }

            if (tempPath == null)
            {
                // If we don't have a temp directory, compilation can't proceed. This shouldn't ever happen,
                // because our clients always send the temp directory.
                Debug.Assert(false, "Client did not send temp directory; this is required.");
                return new CompletedBuildResponse(-1, utf8output: false, output: "", errorOutput: "");
            }

            TextWriter output = new StringWriter(CultureInfo.InvariantCulture);
            bool utf8output;
            int returnCode = BasicCompile(
                _responseFileDirectory,
                currentDirectory,
                libDirectory,
                tempPath,
                commandLineArguments,
                output,
                cancellationToken,
                out utf8output);

            return new CompletedBuildResponse(returnCode, utf8output, output.ToString(), "");
        }