/// <summary>
        /// Don't wait on system objects if we don't have to - see if we have work to do.
        /// </summary>
        /// <param name="executionMode"></param>
        /// <param name="index"></param>
        /// <returns></returns>
        private bool WaitAnyFast(NodeLoopExecutionMode executionMode, out int index)
        {
            index = 0;

            if (exitTaskThreadsCache.exitTaskThreads)
            {
                return(true);
            }

            if (executionMode == NodeLoopExecutionMode.BaseActiveThread)
            {
                lock (workItemQueue)
                {
                    if (workItemQueue.Count > 0)
                    {
                        index = 1;
                        return(true);
                    }
                }
            }
            else if (executionMode == NodeLoopExecutionMode.WaitingActiveThread)
            {
                lock (workItemQueue)
                {
                    if (workItemQueue.Count > 0)
                    {
                        index = 1;
                        return(true);
                    }
                }
                lock (postedBuildResults)
                {
                    if (postedBuildResults.Count > 0)
                    {
                        index = 2;
                        return(true);
                    }
                }
            }
            else if (executionMode == NodeLoopExecutionMode.WaitingPassiveThread)
            {
                lock (postedBuildResults)
                {
                    if (postedBuildResults.Count > 0)
                    {
                        index = 1;
                        return(true);
                    }
                }
            }
            else
            {
                ErrorUtilities.VerifyThrow(false, "Unexpected NodeLoopExecutionMode");
            }

            return(false);
        }
        /// <summary>
        /// This function calculates the array of events the thread should wait on depending on its
        /// execution mode
        /// </summary>
        /// <param name="executionMode">Current execution mode</param>
        /// <returns>Array of handles to wait on</returns>
        private WaitHandle [] GetHandlesArray(NodeLoopExecutionMode executionMode)
        {
            WaitHandle[] waitHandles = null;

            if (executionMode == NodeLoopExecutionMode.BaseActiveThread)
            {
                if (BaseActiveThreadWaitHandles == null)
                {
                    BaseActiveThreadWaitHandles    = new WaitHandle[2];
                    BaseActiveThreadWaitHandles[0] = exitTaskThreads;
                    BaseActiveThreadWaitHandles[1] = workItemInsertionEvent;
                }
                waitHandles = BaseActiveThreadWaitHandles;
            }
            else if (executionMode == NodeLoopExecutionMode.WaitingActiveThread)
            {
                if (WaitingActiveThreadWaitHandles == null)
                {
                    WaitingActiveThreadWaitHandles    = new WaitHandle[3];
                    WaitingActiveThreadWaitHandles[0] = exitTaskThreads;
                    WaitingActiveThreadWaitHandles[1] = workItemInsertionEvent;
                    WaitingActiveThreadWaitHandles[2] = localDoneNoticeEvent;
                }
                waitHandles = WaitingActiveThreadWaitHandles;
            }
            else if (executionMode == NodeLoopExecutionMode.WaitingPassiveThread)
            {
                if (WaitingPassiveThreadWaitHandles == null)
                {
                    WaitingPassiveThreadWaitHandles    = new WaitHandle[2];
                    WaitingPassiveThreadWaitHandles[0] = exitTaskThreads;
                    WaitingPassiveThreadWaitHandles[1] = localDoneNoticeEvent;
                }
                waitHandles = WaitingPassiveThreadWaitHandles;
            }
            else
            {
                ErrorUtilities.VerifyThrow(false, "Unexpected NodeLoopExecutionMode");
            }

            return(waitHandles);
        }
        /// <summary>
        /// This is the loop for all active threads. Depending on the current execution mode the thread
        /// will listen to different events. There is only one thread at the time that owns the workitem
        /// queue and listens for tasks to be executed. There is also only one thread at a time that is
        /// execution a task. That thread owns the current directory and the environment block.
        /// </summary>
        private void NodeActionLoop
        (
            NodeLoopExecutionMode executionMode,
            int handleId,
            BuildResult [] buildResults
        )
        {
            // Create an array of event to the node thread responds
            WaitHandle[] waitHandles = GetHandlesArray(executionMode);

            int  resultCount = 0;
            long entryTime   = 0;

            // A thread that is waiting for a done notification is no longer
            // actively executing a task so the active cound needs to be decreased
            if (executionMode != NodeLoopExecutionMode.BaseActiveThread)
            {
                parentModule.DecrementActiveThreadCount();
                // If requested measure the time spent waiting for the results
                if (profileExecution)
                {
                    entryTime = DateTime.Now.Ticks;
                }
            }

            bool continueExecution = true;

            while (continueExecution)
            {
                int eventType;

                // Try and avoid the wait on kernel objects if possible.
                if (!WaitAnyFast(executionMode, out eventType))
                {
                    eventType = WaitHandle.WaitAny(waitHandles);
                }

                if (Engine.debugMode)
                {
                    Console.WriteLine("TaskWorkerThread: HandleId " + handleId + " waking up due to event type " + eventType);
                }

                // Node exit event - all threads need to exit
                if (eventType == 0)
                {
                    continueExecution = false;
                }
                // New work item has appeared in the queue
                else if (eventType == 1 && executionMode != NodeLoopExecutionMode.WaitingPassiveThread)
                {
                    ErrorUtilities.VerifyThrow(
                        executionMode == NodeLoopExecutionMode.WaitingActiveThread ||
                        executionMode == NodeLoopExecutionMode.BaseActiveThread,
                        "Only active threads should receive work item events");

                    if (executionMode == NodeLoopExecutionMode.BaseActiveThread)
                    {
                        // Wait until all there are no other active threads, we
                        // always transition from 0 to 1 atomically before executing the task
                        parentModule.WaitForZeroActiveThreadCount();

                        TaskExecutionState taskToExecute = null;
                        lock (workItemQueue)
                        {
                            taskToExecute = workItemQueue.Dequeue();
                            // We may get a single event for multiple messages so only reset the event
                            // if the queue is empty
                            if (workItemQueue.Count == 0)
                            {
                                workItemInsertionEvent.Reset();
                            }
                        }

                        // Execute the task either directly or on a child thread
                        ErrorUtilities.VerifyThrow(taskToExecute != null, "Expected a workitem");

                        handleIdToWorkerThread[taskToExecute.HandleId] = this;
                        currentWorkitem = taskToExecute;

                        // Actually execute the task (never throws - all exceptions are captured)
                        taskToExecute.ExecuteTask();

                        currentWorkitem = null;
                        handleIdToWorkerThread.Remove(taskToExecute.HandleId);

                        // Indicate that this thread is no longer active
                        parentModule.DecrementActiveThreadCount();
                    }
                    else
                    {
                        // Change the thread execution mode since it will no longer be
                        // listening to the work item queue
                        executionMode = NodeLoopExecutionMode.WaitingPassiveThread;
                        threadActive  = false;
                        waitHandles   = GetHandlesArray(executionMode);

                        TaskWorkerThread workerThread = null;
                        lock (workerThreadQueue)
                        {
                            if (workerThreadQueue.Count != 0)
                            {
                                //Console.WriteLine("REUSING a thread");
                                workerThread = workerThreadQueue.Dequeue();
                            }
                        }
                        if (workerThread == null)
                        {
                            //Console.WriteLine("CREATING a thread");
                            workerThread = new TaskWorkerThread(parentModule, exitTaskThreads, exitTaskThreadsCache, workerThreadQueue, handleIdToWorkerThread,
                                                                workItemQueue, workItemInsertionEvent, waitingTasks, profileExecution);
                        }

                        workerThread.ActivateThread();
                    }
                }
                else if ((eventType == 1 && executionMode == NodeLoopExecutionMode.WaitingPassiveThread) ||
                         (eventType == 2 && executionMode == NodeLoopExecutionMode.WaitingActiveThread))
                {
                    // There maybe multiple results in the list so we need to loop over it
                    // and store the results
                    int originalResultCount = resultCount;
                    lock (postedBuildResults)
                    {
                        LinkedListNode <BuildResult> currentNode = postedBuildResults.First;
                        while (currentNode != null)
                        {
                            BuildResult buildResult = currentNode.Value;
                            ErrorUtilities.VerifyThrow(
                                buildResult.RequestId < buildResults.Length,
                                "The request ids should be inside the array");
                            buildResults[buildResult.RequestId] = buildResult;
                            // Increment the result count to indicate that we got another result
                            resultCount++;
                            // Go to the next entry in the list (most of the time there will be just one entry)
                            currentNode = currentNode.Next;
                        }
                        postedBuildResults.Clear();
                        // Reset the handle now that we done with the events
                        int handleIndex = executionMode == NodeLoopExecutionMode.WaitingPassiveThread ? 1 : 2;
                        ((ManualResetEvent)waitHandles[handleIndex]).Reset();
                    }
                    ErrorUtilities.VerifyThrow(originalResultCount < resultCount, "We should have found at least 1 result");
                    // If we received results for all the requests we need to exit
                    if (resultCount == buildResults.Length)
                    {
                        continueExecution = false;
                    }
                }
                // Check if we need to update the state
                if (executionMode == NodeLoopExecutionMode.BaseActiveThread && !threadActive)
                {
                    continueExecution = false;
                }
            }

            ErrorUtilities.VerifyThrow
                (resultCount == 0 || executionMode != NodeLoopExecutionMode.BaseActiveThread,
                "The base thread should never return a value");

            // If a thread exits this loop it is back to actively executing the task,
            // so the active thread count has to be increased
            if (executionMode != NodeLoopExecutionMode.BaseActiveThread)
            {
                parentModule.WaitForZeroActiveThreadCount();
                // Sent the time spent waiting for results to the ExecutionState so that the task execution time can be measured correctly
                if (profileExecution)
                {
                    this.currentWorkitem.NotifyOfWait(entryTime);
                }
            }
        }
Exemple #4
0
        /// <summary>
        /// This is the loop for all active threads. Depending on the current execution mode the thread
        /// will listen to different events. There is only one thread at the time that owns the workitem
        /// queue and listens for tasks to be executed. There is also only one thread at a time that is
        /// execution a task. That thread owns the current directory and the environment block.
        /// </summary>
        /// <param name="executionMode"></param>
        /// <param name="nodeProxyId"></param>
        /// <param name="requestResults"></param>
        private void NodeActionLoop(NodeLoopExecutionMode executionMode, int nodeProxyId, BuildResult [] requestResults)
        {
            // Create an array of event to the node thread responds
            WaitHandle[] waitHandles = GetHandlesArray(executionMode);

            int resultCount = 0;

            // A thread that is waiting for a done notification is no longer
            // actively executing a task so the active cound needs to be decreased
            if (executionMode != NodeLoopExecutionMode.BaseActiveThread)
            {
                Interlocked.Decrement(ref activeThreadCount);
                threadCountEvent.Set();
            }

            bool continueExecution = true;

            while (continueExecution)
            {
                int eventType = WaitHandle.WaitAny(waitHandles);

                if (Environment.GetEnvironmentVariable("MSBUILDDEBUG") == "1")
                {
                    Console.WriteLine("NodeProxy :" + nodeProxyId + " waking up due " + eventType);
                }

                // Node exit event - all threads need to exit
                if (eventType == 0)
                {
                    continueExecution = false;
                }
                // New work item has appeared in the queue
                else if (eventType == 1 && executionMode != NodeLoopExecutionMode.WaitingPassiveThread)
                {
                    ErrorUtilities.VerifyThrow(
                        executionMode == NodeLoopExecutionMode.WaitingActiveThread ||
                        executionMode == NodeLoopExecutionMode.BaseActiveThread,
                        "Only active threads should receive work item events");

                    if (executionMode == NodeLoopExecutionMode.BaseActiveThread)
                    {
                        TaskExecutionState taskToExecute = null;
                        lock (workItemQueue)
                        {
                            taskToExecute = workItemQueue.Dequeue();
                            // We may get a single event for multiple messages so only reset the event
                            // if the queue is empty
                            if (workItemQueue.Count == 0)
                            {
                                workItemInsertionEvent.Reset();
                            }
                        }

                        // Execute the task either directly or on a child thread
                        ErrorUtilities.VerifyThrow(taskToExecute != null, "Expected a workitem");

                        // Wait until all there are no other active threads, we
                        // always transition from 0 to 1 atomically before executing the task
                        while (Interlocked.CompareExchange(ref activeThreadCount, 1, 0) != 0)
                        {
                            threadCountEvent.WaitOne();
                            threadCountEvent.Reset();
                        }

                        proxyIdToWorkerThread[taskToExecute.NodeProxyId] = this;

                        // Actually execute the task
                        taskToExecute.ExecuteTask();

                        proxyIdToWorkerThread.Remove(taskToExecute.NodeProxyId);

                        // Indicate that this thread is no longer active
                        Interlocked.Decrement(ref activeThreadCount);
                        threadCountEvent.Set();
                    }
                    else
                    {
                        // Change the thread execution mode since it will no longer be
                        // listening to the work item queue
                        executionMode = NodeLoopExecutionMode.WaitingPassiveThread;
                        threadActive  = false;
                        waitHandles   = GetHandlesArray(executionMode);

                        TaskWorkerThread workerThread = null;
                        lock (workerThreadQueue)
                        {
                            if (workerThreadQueue.Count != 0)
                            {
                                //Console.WriteLine("REUSING a thread");
                                workerThread = workerThreadQueue.Dequeue();
                            }
                        }
                        if (workerThread == null)
                        {
                            //Console.WriteLine("CREATING a thread");
                            workerThread = new TaskWorkerThread();
                        }

                        workerThread.ActivateThread();
                    }
                }
                else if (eventType == 1 && executionMode == NodeLoopExecutionMode.WaitingPassiveThread ||
                         eventType == 2 && executionMode == NodeLoopExecutionMode.WaitingActiveThread)
                {
                    // There maybe multiple results in the list so we need to loop over it
                    // and store the results
                    int originalResultCount = resultCount;
                    lock (targetEvaluationResults)
                    {
                        //Console.WriteLine("Worker thread for: " + nodeProxyId + " Got results");
                        LinkedListNode <BuildResult> currentNode = targetEvaluationResults.First;
                        while (currentNode != null)
                        {
                            BuildResult buildResult = currentNode.Value;
                            ErrorUtilities.VerifyThrow(
                                buildResult.RequestId < requestResults.Length,
                                "The request ids should be inside the array");
                            requestResults[buildResult.RequestId] = buildResult;
                            // Increment the result count to indicate that we got another result
                            resultCount++;
                            // Go to the next entry in the list (most of the time there will be just one entry)
                            currentNode = currentNode.Next;
                        }
                        targetEvaluationResults.Clear();
                        // Reset the handle now that we done with the events
                        int handleIndex = executionMode == NodeLoopExecutionMode.WaitingPassiveThread ? 1 : 2;
                        ((ManualResetEvent)waitHandles[handleIndex]).Reset();
                    }
                    ErrorUtilities.VerifyThrow(originalResultCount < resultCount, "We should have found at least 1 result");
                    // If we received results for all the requests we need to exit
                    if (resultCount == requestResults.Length)
                    {
                        continueExecution = false;
                    }
                }
                // Check if we need to update the state
                if (executionMode == NodeLoopExecutionMode.BaseActiveThread && !threadActive)
                {
                    continueExecution = false;
                }
            }

            ErrorUtilities.VerifyThrow
                (resultCount == 0 || executionMode != NodeLoopExecutionMode.BaseActiveThread,
                "The base thread should never return a value");

            // If a thread exits this loop it is back to actively executing the task,
            // so the active thread count has to be increased
            if (executionMode != NodeLoopExecutionMode.BaseActiveThread)
            {
                while (Interlocked.CompareExchange(ref activeThreadCount, 1, 0) != 0)
                {
                    threadCountEvent.WaitOne();
                    threadCountEvent.Reset();
                }
            }
        }
Exemple #5
0
        /// <summary>
        /// This is the loop for all active threads. Depending on the current execution mode the thread
        /// will listen to different events. There is only one thread at the time that owns the workitem
        /// queue and listens for tasks to be executed. There is also only one thread at a time that is
        /// execution a task. That thread owns the current directory and the environment block.
        /// </summary>
        /// <param name="executionMode"></param>
        /// <param name="nodeProxyId"></param>
        /// <param name="requestResults"></param>
        private void NodeActionLoop(NodeLoopExecutionMode executionMode, int nodeProxyId, BuildResult [] requestResults)
        {
            // Create an array of event to the node thread responds
            WaitHandle[] waitHandles = GetHandlesArray(executionMode);

            int resultCount = 0;

            // A thread that is waiting for a done notification is no longer
            // actively executing a task so the active cound needs to be decreased
            if (executionMode != NodeLoopExecutionMode.BaseActiveThread)
            {
                Interlocked.Decrement(ref activeThreadCount);
                threadCountEvent.Set();
            }

            bool continueExecution = true;
            while (continueExecution)
            {
                int eventType = WaitHandle.WaitAny(waitHandles);

                if (Environment.GetEnvironmentVariable("MSBUILDDEBUG") == "1")
                {
                    Console.WriteLine("NodeProxy :" + nodeProxyId + " waking up due " + eventType);
                }

                // Node exit event - all threads need to exit
                if (eventType == 0)
                {
                    continueExecution = false;
                }
                // New work item has appeared in the queue
                else if (eventType == 1 && executionMode != NodeLoopExecutionMode.WaitingPassiveThread )
                {
                    ErrorUtilities.VerifyThrow(
                                    executionMode == NodeLoopExecutionMode.WaitingActiveThread ||
                                    executionMode == NodeLoopExecutionMode.BaseActiveThread,
                                    "Only active threads should receive work item events");

                    if (executionMode == NodeLoopExecutionMode.BaseActiveThread)
                    {
                        TaskExecutionState taskToExecute = null;
                        lock (workItemQueue)
                        {
                            taskToExecute = workItemQueue.Dequeue();
                            // We may get a single event for multiple messages so only reset the event
                            // if the queue is empty
                            if (workItemQueue.Count == 0)
                            {
                                workItemInsertionEvent.Reset();
                            }
                        }

                        // Execute the task either directly or on a child thread
                        ErrorUtilities.VerifyThrow(taskToExecute != null, "Expected a workitem");

                        // Wait until all there are no other active threads, we
                        // always transition from 0 to 1 atomically before executing the task
                        while (Interlocked.CompareExchange(ref activeThreadCount, 1, 0) != 0)
                        {
                            threadCountEvent.WaitOne();
                            threadCountEvent.Reset();
                        }

                        proxyIdToWorkerThread[taskToExecute.NodeProxyId] = this;

                        // Actually execute the task
                        taskToExecute.ExecuteTask();

                        proxyIdToWorkerThread.Remove(taskToExecute.NodeProxyId);

                        // Indicate that this thread is no longer active
                        Interlocked.Decrement(ref activeThreadCount);
                        threadCountEvent.Set();
                    }
                    else
                    {
                        // Change the thread execution mode since it will no longer be
                        // listening to the work item queue
                        executionMode = NodeLoopExecutionMode.WaitingPassiveThread;
                        threadActive = false;
                        waitHandles = GetHandlesArray(executionMode);

                        TaskWorkerThread workerThread = null;
                        lock (workerThreadQueue)
                        {
                            if (workerThreadQueue.Count != 0)
                            {
                                //Console.WriteLine("REUSING a thread");
                                workerThread = workerThreadQueue.Dequeue();
                            }
                        }
                        if (workerThread == null)
                        {
                            //Console.WriteLine("CREATING a thread");
                            workerThread = new TaskWorkerThread();
                        }

                        workerThread.ActivateThread();
                    }
                }
                else if (eventType == 1 && executionMode == NodeLoopExecutionMode.WaitingPassiveThread ||
                         eventType == 2 && executionMode == NodeLoopExecutionMode.WaitingActiveThread)
                {
                    // There maybe multiple results in the list so we need to loop over it 
                    // and store the results
                    int originalResultCount = resultCount;
                    lock (targetEvaluationResults)
                    {
                        //Console.WriteLine("Worker thread for: " + nodeProxyId + " Got results");
                        LinkedListNode<BuildResult> currentNode = targetEvaluationResults.First;
                        while (currentNode != null)
                        {   
                            BuildResult buildResult = currentNode.Value;
                            ErrorUtilities.VerifyThrow(
                                            buildResult.RequestId < requestResults.Length,
                                            "The request ids should be inside the array");
                            requestResults[buildResult.RequestId] = buildResult;
                            // Increment the result count to indicate that we got another result
                            resultCount++;
                            // Go to the next entry in the list (most of the time there will be just one entry)
                            currentNode = currentNode.Next;
                        }
                        targetEvaluationResults.Clear();
                        // Reset the handle now that we done with the events
                        int handleIndex = executionMode == NodeLoopExecutionMode.WaitingPassiveThread ? 1 : 2;
                        ((ManualResetEvent)waitHandles[handleIndex]).Reset();
                    }
                    ErrorUtilities.VerifyThrow(originalResultCount < resultCount, "We should have found at least 1 result");
                    // If we received results for all the requests we need to exit
                    if (resultCount == requestResults.Length)
                    {
                        continueExecution = false;
                    }
                }
                // Check if we need to update the state
                if (executionMode == NodeLoopExecutionMode.BaseActiveThread && !threadActive) 
                {
                    continueExecution = false;
                }
            }

            ErrorUtilities.VerifyThrow
                (resultCount == 0 || executionMode != NodeLoopExecutionMode.BaseActiveThread,
                 "The base thread should never return a value");

            // If a thread exits this loop it is back to actively executing the task,
            // so the active thread count has to be increased
            if (executionMode != NodeLoopExecutionMode.BaseActiveThread)
            {
                while (Interlocked.CompareExchange(ref activeThreadCount, 1, 0) != 0)
                {
                    threadCountEvent.WaitOne();
                    threadCountEvent.Reset();
                }
            }
        }
Exemple #6
0
        /// <summary>
        /// This function calculates the array of events the thread should wait on depending on its
        /// execution mode
        /// </summary>
        /// <param name="executionMode">Current execution mode</param>
        /// <returns>Array of handles to wait on</returns>
        private WaitHandle [] GetHandlesArray( NodeLoopExecutionMode executionMode )
        {
            WaitHandle[] waitHandles = null;

            if (executionMode == NodeLoopExecutionMode.BaseActiveThread)
            {
                if (BaseActiveThreadWaitHandles == null)
                {
                    BaseActiveThreadWaitHandles = new WaitHandle[2];
                    BaseActiveThreadWaitHandles[0] = exitTaskThreads;
                    BaseActiveThreadWaitHandles[1] = workItemInsertionEvent;
                }
                waitHandles = BaseActiveThreadWaitHandles;
            }
            else if (executionMode == NodeLoopExecutionMode.WaitingActiveThread)
            {
                if (WaitingActiveThreadWaitHandles == null)
                {
                    WaitingActiveThreadWaitHandles = new WaitHandle[3];
                    WaitingActiveThreadWaitHandles[0] = exitTaskThreads;
                    WaitingActiveThreadWaitHandles[1] = workItemInsertionEvent;
                    WaitingActiveThreadWaitHandles[2] = localDoneNoticeEvent;
                }
                waitHandles = WaitingActiveThreadWaitHandles;
            }
            else if (executionMode == NodeLoopExecutionMode.WaitingPassiveThread)
            {
                if (WaitingPassiveThreadWaitHandles == null)
                {
                    WaitingPassiveThreadWaitHandles = new WaitHandle[2];
                    WaitingPassiveThreadWaitHandles[0] = exitTaskThreads;
                    WaitingPassiveThreadWaitHandles[1] = localDoneNoticeEvent;
                }
                waitHandles = WaitingPassiveThreadWaitHandles;
            }
            else
            {
                ErrorUtilities.VerifyThrow(false, "Unexpected NodeLoopExecutionMode");
            }

            return waitHandles;
        }
Exemple #7
0
        /// <summary>
        /// This is the loop for all active threads. Depending on the current execution mode the thread
        /// will listen to different events. There is only one thread at the time that owns the workitem
        /// queue and listens for tasks to be executed. There is also only one thread at a time that is
        /// execution a task. That thread owns the current directory and the environment block.
        /// </summary>
        private void NodeActionLoop
        (
            NodeLoopExecutionMode executionMode, 
            int handleId, 
            BuildResult [] buildResults
        )
        {

            // Create an array of event to the node thread responds
            WaitHandle[] waitHandles = GetHandlesArray(executionMode);

            int resultCount = 0;
            long entryTime = 0;

            // A thread that is waiting for a done notification is no longer
            // actively executing a task so the active cound needs to be decreased
            if (executionMode != NodeLoopExecutionMode.BaseActiveThread)
            {
                parentModule.DecrementActiveThreadCount();
                // If requested measure the time spent waiting for the results
                if (profileExecution)
                {
                    entryTime = DateTime.Now.Ticks;
                }
            }
            
            bool continueExecution = true;
            while (continueExecution)
            {
                int eventType;

                // Try and avoid the wait on kernel objects if possible.
                if (!WaitAnyFast(executionMode, out eventType))
                {
                    eventType = WaitHandle.WaitAny(waitHandles);
                }

                if (Engine.debugMode)
                {
                    Console.WriteLine("TaskWorkerThread: HandleId " + handleId + " waking up due to event type " + eventType);
                }

                // Node exit event - all threads need to exit
                if (eventType == 0)
                {
                    continueExecution = false;
                }
                // New work item has appeared in the queue
                else if (eventType == 1 && executionMode != NodeLoopExecutionMode.WaitingPassiveThread )
                {
                    ErrorUtilities.VerifyThrow(
                                    executionMode == NodeLoopExecutionMode.WaitingActiveThread ||
                                    executionMode == NodeLoopExecutionMode.BaseActiveThread,
                                    "Only active threads should receive work item events");

                    if (executionMode == NodeLoopExecutionMode.BaseActiveThread)
                    {
                        // Wait until all there are no other active threads, we
                        // always transition from 0 to 1 atomically before executing the task
                        parentModule.WaitForZeroActiveThreadCount();

                        TaskExecutionState taskToExecute = null;
                        lock (workItemQueue)
                        {
                            taskToExecute = workItemQueue.Dequeue();
                            // We may get a single event for multiple messages so only reset the event
                            // if the queue is empty
                            if (workItemQueue.Count == 0)
                            {
                                workItemInsertionEvent.Reset();
                            }
                        }

                        // Execute the task either directly or on a child thread
                        ErrorUtilities.VerifyThrow(taskToExecute != null, "Expected a workitem");

                        handleIdToWorkerThread[taskToExecute.HandleId] = this;
                        currentWorkitem = taskToExecute;

                        // Actually execute the task (never throws - all exceptions are captured)
                        taskToExecute.ExecuteTask();

                        currentWorkitem = null;
                        handleIdToWorkerThread.Remove(taskToExecute.HandleId);

                        // Indicate that this thread is no longer active
                        parentModule.DecrementActiveThreadCount();
                    }
                    else
                    {
                        // Change the thread execution mode since it will no longer be
                        // listening to the work item queue
                        executionMode = NodeLoopExecutionMode.WaitingPassiveThread;
                        threadActive = false;
                        waitHandles = GetHandlesArray(executionMode);

                        TaskWorkerThread workerThread = null;
                        lock (workerThreadQueue)
                        {
                            if (workerThreadQueue.Count != 0)
                            {
                                //Console.WriteLine("REUSING a thread");
                                workerThread = workerThreadQueue.Dequeue();
                            }
                        }
                        if (workerThread == null)
                        {
                            //Console.WriteLine("CREATING a thread");
                            workerThread = new TaskWorkerThread(parentModule, exitTaskThreads, exitTaskThreadsCache, workerThreadQueue, handleIdToWorkerThread,
                                                                workItemQueue, workItemInsertionEvent, waitingTasks, profileExecution);
                        }

                        workerThread.ActivateThread();
                    }
                }
                else if (eventType == 1 && executionMode == NodeLoopExecutionMode.WaitingPassiveThread ||
                         eventType == 2 && executionMode == NodeLoopExecutionMode.WaitingActiveThread)
                {
                    // There maybe multiple results in the list so we need to loop over it 
                    // and store the results
                    int originalResultCount = resultCount;
                    lock (postedBuildResults)
                    {
                        LinkedListNode<BuildResult> currentNode = postedBuildResults.First;
                        while (currentNode != null)
                        {   
                            BuildResult buildResult = currentNode.Value;
                            ErrorUtilities.VerifyThrow(
                                            buildResult.RequestId < buildResults.Length,
                                            "The request ids should be inside the array");
                            buildResults[buildResult.RequestId] = buildResult;
                            // Increment the result count to indicate that we got another result
                            resultCount++;
                            // Go to the next entry in the list (most of the time there will be just one entry)
                            currentNode = currentNode.Next;
                        }
                        postedBuildResults.Clear();
                        // Reset the handle now that we done with the events
                        int handleIndex = executionMode == NodeLoopExecutionMode.WaitingPassiveThread ? 1 : 2;
                        ((ManualResetEvent)waitHandles[handleIndex]).Reset();
                    }
                    ErrorUtilities.VerifyThrow(originalResultCount < resultCount, "We should have found at least 1 result");
                    // If we received results for all the requests we need to exit
                    if (resultCount == buildResults.Length)
                    {
                        continueExecution = false;
                    }
                }
                // Check if we need to update the state
                if (executionMode == NodeLoopExecutionMode.BaseActiveThread && !threadActive) 
                {
                    continueExecution = false;
                }
            }

            ErrorUtilities.VerifyThrow
                (resultCount == 0 || executionMode != NodeLoopExecutionMode.BaseActiveThread,
                 "The base thread should never return a value");

            // If a thread exits this loop it is back to actively executing the task,
            // so the active thread count has to be increased
            if (executionMode != NodeLoopExecutionMode.BaseActiveThread)
            {
                parentModule.WaitForZeroActiveThreadCount();
                // Sent the time spent waiting for results to the ExecutionState so that the task execution time can be measured correctly
                if (profileExecution)
                {
                    this.currentWorkitem.NotifyOfWait(entryTime);
                }
            }
        }
Exemple #8
0
        /// <summary>
        /// Don't wait on system objects if we don't have to - see if we have work to do.
        /// </summary>
        /// <param name="executionMode"></param>
        /// <param name="index"></param>
        /// <returns></returns>
        private bool WaitAnyFast(NodeLoopExecutionMode executionMode, out int index)
        {
            index = 0;

            if (exitTaskThreadsCache.exitTaskThreads)
            {
                return true;
            }

            if (executionMode == NodeLoopExecutionMode.BaseActiveThread)
            {
                lock (workItemQueue)
                {
                    if (workItemQueue.Count > 0)
                    {
                        index = 1;
                        return true;
                    }
                }
            }
            else if (executionMode == NodeLoopExecutionMode.WaitingActiveThread)
            {
                lock (workItemQueue)
                {
                    if (workItemQueue.Count > 0)
                    {
                        index = 1;
                        return true;
                    }
                }
                lock (postedBuildResults)
                {
                    if (postedBuildResults.Count > 0)
                    {
                        index = 2;
                        return true;
                    }
                }
            }
            else if (executionMode == NodeLoopExecutionMode.WaitingPassiveThread)
            {
                lock (postedBuildResults)
                {
                    if (postedBuildResults.Count > 0)
                    {
                        index = 1;
                        return true;
                    }
                }
            }
            else
            {
                ErrorUtilities.VerifyThrow(false, "Unexpected NodeLoopExecutionMode");
            }

            return false;
        }