Exemple #1
0
        /// <summary>
        /// Called to execute a task within a target. This method instantiates the task, sets its parameters,
        /// and executes it.
        /// </summary>
        /// <param name="taskState"></param>
        public void ExecuteTask(TaskExecutionState taskState)
        {
            // Fill out the local fields of the task state
            taskState.ParentModule     = this;
            taskState.LoggingService   = engineCallback.GetParentEngine().LoggingServices;
            taskState.ProfileExecution = profileExecution;

            // If we running in single proc mode, we should execute this task on the current thread
            if (moduleMode == TaskExecutionModuleMode.SingleProcMode)
            {
                taskState.ExecuteTask();
            }
            else
            {
                // In multiproc mode post the work item to the workerThread queue so it can be executed by the worker thread.
                workerThread.PostWorkItem(taskState);
            }
        }
Exemple #2
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 #3
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();
                }
            }
        }