internal BuildResult EngineBuildLoop()

in src/Deprecated/Engine/Engine/Engine.cs [1622:1854]


        internal BuildResult EngineBuildLoop(BuildRequest terminatingBuildRequest)
        {
            ErrorUtilities.VerifyThrow(this.numberOfProjectsInProgress == 0 || terminatingBuildRequest != null,
                                       "We can only call this method once");

            // Create an array of events to which this thread responds
            WaitHandle[] waitHandles = new WaitHandle[5];
            waitHandles[0] = engineAbortEvent;                  // Exit event
            waitHandles[1] = engineCommands.QueueReadyEvent;    // New engine command
            waitHandles[2] = buildRequests.QueueReadyEvent;     // New build request
            waitHandles[3] = taskOutputUpdates.QueueReadyEvent; // New task outputs
            waitHandles[4] = flushRequestEvent;                 // A logging service needs a flush

            BuildResult buildResult = null;
            bool continueExecution = true;
            lastLoopActivity = DateTime.Now.Ticks;
            int loopTimeout= Introspector.initialLoopTimeout;   // Inactivity timeout which triggers deadlock check
            int loopTimeoutRemaining = Introspector.initialLoopTimeout;
            int flushTimeout = EngineLoggingServices.flushTimeoutInMS; // Timeout with which the log is flushed
            bool forceFlush = false;
            while (
                    continueExecution && 
                    (terminatingBuildRequest == null || terminatingBuildRequest.BuildCompleted == false)
                  )
            {
                int eventType = 0;

                // See if we have anything to do without waiting on the handles which is expensive 
                // for kernel mode objects.
                if (this.engineAbortCachedValue == true)
                {
                    eventType = 0;
                }
                else if (engineCommands.Count > 0)
                {
                    eventType = 1;
                }
                else if (buildRequests.Count > 0)
                {
                    eventType = 2;
                }
                else if (taskOutputUpdates.Count > 0)
                {
                    eventType = 3;
                }
                else if (primaryLoggingServices.NeedsFlush(lastLoopActivity))
                {
                    eventType = 4;
                }
                else
                {
                    // Nothing going on at the moment, go to sleep and wait for something to happen
                    eventType = WaitHandle.WaitAny(waitHandles, flushTimeout, false);
                }

                if (eventType == WaitHandle.WaitTimeout)
                {
                    // Decrement time remaining until deadlock check
                    if (loopTimeoutRemaining != Timeout.Infinite)
                    {
                        loopTimeoutRemaining = flushTimeout > loopTimeoutRemaining ? 
                                                  0 : loopTimeoutRemaining - flushTimeout;
                    }
                    // Always force a flush on a time
                    forceFlush = true;

                    // If time has run out perform deadlock check
                    if (loopTimeoutRemaining == 0)
                    {
                        loopTimeout =
                            introspector.DetectDeadlock(buildRequests.Count + this.taskOutputUpdates.Count,
                                                        lastLoopActivity, loopTimeout);
                        loopTimeoutRemaining = loopTimeout;
                    }
                }
                else if (eventType == 0)
                {
                    continueExecution = false;
                    SetEngineAbortTo(false);
                }
                // Received an engine command
                else if (eventType == 1)
                {
                    EngineCommand engineCommand = this.engineCommands.Dequeue();
                    ErrorUtilities.VerifyThrow(engineCommand != null, "Should have gotten a command");
                    // Execute the command
                    engineCommand.Execute(this);

                    // Don't consider node status request to be activity 
                    if (!(engineCommand is RequestStatusEngineCommand))
                    {
                        lastLoopActivity = DateTime.Now.Ticks;
                        loopTimeoutRemaining = loopTimeout;
                    }
                }
                // New build requests have been posted
                else if (eventType == 2)
                {
                    BuildRequest currentRequest = this.buildRequests.Dequeue();
                    ErrorUtilities.VerifyThrow(currentRequest != null, "Should have gotten an evalution request");

                    //Console.WriteLine( "Child mode: " + Scheduler.ChildMode +" Got request to build " + currentRequest.GetTargetNamesList() + " in " + currentRequest.ProjectFileName + " Time: " + DateTime.Now.ToLongTimeString() + ":" + DateTime.Now.Millisecond);

                    if (!currentRequest.BuildCompleted)
                    {
                        if (currentRequest.ProjectToBuild != null)
                        {
                            Scheduler.NotifyOfSchedulingDecision(currentRequest, EngineCallback.inProcNode);
                            BuildProjectInternal(currentRequest, null, null, true);
                        }
                        else
                        {
                            BuildProjectFileInternal(currentRequest);
                        }
                    }
                    else
                    {
                        InvalidProjectFileException projectException = currentRequest.BuildException;
                        primaryLoggingServices.LogInvalidProjectFileError(currentRequest.ParentBuildEventContext, projectException);
                        Scheduler.NotifyOfSchedulingDecision(currentRequest, this.nodeId);
                        HandleProjectFileInternalException(currentRequest);
                    }
                    lastLoopActivity = DateTime.Now.Ticks;
                    loopTimeoutRemaining = loopTimeout;
                }
                // New task outputs have been posted
                else if (eventType == 3)
                {
                    TaskExecutionContext taskExecutionContext = this.taskOutputUpdates.Dequeue();
                    ErrorUtilities.VerifyThrow(taskExecutionContext != null, "Should have gotten a task update");

                    // Clear the node proxy state, all the write to the proxy state should come from the engine thread
                    EngineCallback.ClearContextState(taskExecutionContext.HandleId);

                    if (Engine.debugMode)
                    {
                        if (taskExecutionContext.BuildContext.BuildRequest != null)
                            Console.WriteLine("NodeId: " + NodeId + " Got output update " + taskExecutionContext.ParentProject.FullFileName + " HandleId: " + taskExecutionContext.BuildContext.BuildRequest.HandleId + " Time: " + DateTime.Now.ToLongTimeString() + ":" + DateTime.Now.Millisecond);
                        else
                            Console.WriteLine("NodeId: " + NodeId + " Got output update " + taskExecutionContext.ParentProject.FullFileName + " HandleId: None Time: " + DateTime.Now.ToLongTimeString() + ":" + DateTime.Now.Millisecond);
                    }

                    // In inproc scenario we may receive a task done notification for a build context
                    // which has already completed with an exception. In this case we can ignore the
                    // notification because the context is already completed.
                    if (!taskExecutionContext.BuildContext.BuildComplete)
                    {
                        BuildProjectInternal(taskExecutionContext.BuildContext.BuildRequest, taskExecutionContext.BuildContext, taskExecutionContext, false);
                    }
                    else
                    {
                        if (Engine.debugMode)
                        {
                            if (taskExecutionContext.BuildContext.BuildRequest != null)
                                Console.WriteLine("Ignoring task output notification. NodeId: " + NodeId + " Got output update " + taskExecutionContext.ParentProject.FullFileName + " HandleId: " + taskExecutionContext.BuildContext.BuildRequest.HandleId);
                            else
                                Console.WriteLine("Ignoring task output notification. NodeId: " + NodeId + " Got output update " + taskExecutionContext.ParentProject.FullFileName);
                        }
                    }

                    lastLoopActivity = DateTime.Now.Ticks;
                    loopTimeoutRemaining = loopTimeout;
                }
                else if (eventType == 4)
                {
                    // Clear the flush requested event, the logging providers are flushed at the end of the loop
                    flushRequestEvent.Reset();
                    forceFlush = true;
                }
                else
                {
                    ErrorUtilities.VerifyThrow(false, "The event type should be 0, 1, 2 or 3");
                }

                if (NodeManager.TaskExecutionModule == null)
                {
                    // Shutting down, eg due to deadlock. Attempt to flush.
                    forceFlush = true;
                }

                // If necessary flush the queue of logging events (it may have already been flushed recently)
                if (LoggingServices.NeedsFlush(lastLoopActivity) || forceFlush)
                {
                    if (LoggingServices.ProcessPostedLoggingEvents())
                    {
                        lastLoopActivity = DateTime.Now.Ticks;
                        loopTimeoutRemaining = loopTimeout;
                    }
                }

                if (ExternalLoggingServices != null && ( ExternalLoggingServices.NeedsFlush(lastLoopActivity) || forceFlush ))
                {
                    if (ExternalLoggingServices.ProcessPostedLoggingEvents())
                    {
                        lastLoopActivity = DateTime.Now.Ticks;
                        loopTimeoutRemaining = loopTimeout;
                    }
                }

                // Reset the flag forcing the flushing of logging providers
                forceFlush = false;

                // TEM will be null if we're shutting down
                if (NodeManager.TaskExecutionModule != null)
                {
                    if (NodeManager.TaskExecutionModule.UseBreadthFirstTraversal == false /* using depth first traversal */ &&
                        buildRequests.Count == 0 && taskOutputUpdates.Count == 0 &&
                        NodeManager.TaskExecutionModule.IsIdle
                        )
                    {
                        NodeManager.TaskExecutionModule.UseBreadthFirstTraversal = true; /* use breadth first traversal */
                        if (Router.ChildMode)
                        {
                            // Send the status back to the parent as the parent needs to know this node has run out of work, so it can switch the entire system
                            // to breadth first traversal
                            Router.ParentNode.PostStatus(new NodeStatus(true /* use breadth first traversal */), false /* don't block waiting on the send */);
                        }
                        else
                        {
                            // Send the traversal switch directly to all child nodes as the parent has run out of work
                            NodeManager.ChangeNodeTraversalType(true /* use breadth first traversal */);
                        }
                    }
                }
            }

            if (terminatingBuildRequest != null)
            {
                buildResult = terminatingBuildRequest.GetBuildResult();
            }

            return buildResult;
        }