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;
}