/// <summary> /// Indicates to the EngineProxy that it is no longer needed. /// Called by TaskEngine when the task using the EngineProxy is done. /// </summary> internal void MarkAsInActive() { activeProxy = false; // Since the task has a pointer to this class it may store it in a static field. Null out // internal data so the leak of this object doesn't lead to a major memory leak. loggingServices = null; parentModule = null; buildEventContext = null; // Clear out the sponsor (who is responsible for keeping the EngineProxy remoting lease alive until the task is done) // this will be null if the engineproxy was never sent accross an appdomain boundry. if (sponsor != null) { ILease lease = (ILease)RemotingServices.GetLifetimeService(this); if (lease != null) { lease.Unregister(sponsor); } sponsor.Close(); sponsor = null; } }
/// <summary> /// This constructor is used by the class internally to create new instances when a thread /// becomes blocked by a user code callback. /// </summary> private TaskWorkerThread ( TaskExecutionModule parentModule, ManualResetEvent exitTaskThreads, ExitTaskCache exitTaskThreadsCache, Queue <TaskWorkerThread> workerThreadQueue, Hashtable handleIdToWorkerThread, Queue <TaskExecutionState> workItemQueue, ManualResetEvent workItemInsertionEvent, Hashtable waitingTasks, bool profileExecution ) { this.parentModule = parentModule; this.exitTaskThreads = exitTaskThreads; this.exitTaskThreadsCache = exitTaskThreadsCache; this.workerThreadQueue = workerThreadQueue; this.handleIdToWorkerThread = handleIdToWorkerThread; this.workItemQueue = workItemQueue; this.workItemInsertionEvent = workItemInsertionEvent; this.waitingTasks = waitingTasks; this.profileExecution = profileExecution; InitializePerInstanceData(); }
/// <summary> /// Executes a task within a target. This method initializes a task engine for the given task, and then executes the task /// using the engine. /// </summary> /// <param name="taskNode"></param> /// <param name="hostObject"></param> /// <returns>true, if successful</returns> internal bool ExecuteOneTask(XmlElement taskNode, ITaskHost hostObject) { bool taskExecutedSuccessfully = false; string projectFileOfTaskNode = XmlUtilities.GetXmlNodeFile(taskNode, parentProject.FullFileName); BuildEventContext targetBuildEventContext = new BuildEventContext ( ParentProject.ProjectBuildEventContext.NodeId, this.id, ParentProject.ProjectBuildEventContext.ProjectContextId, ParentProject.ProjectBuildEventContext.TaskId ); int handleId = parentEngine.EngineCallback.CreateTaskContext(ParentProject, this, null, taskNode, EngineCallback.inProcNode, targetBuildEventContext); TaskExecutionModule taskExecutionModule = parentEngine.NodeManager.TaskExecutionModule; TaskEngine taskEngine = new TaskEngine(taskNode, hostObject, parentProject.FullFileName, projectFileOfTaskNode, parentEngine.LoggingServices, handleId, taskExecutionModule, targetBuildEventContext); taskExecutedSuccessfully = taskEngine.ExecuteTask ( TaskExecutionMode.ExecuteTaskAndGatherOutputs, new Lookup(parentProject.evaluatedItemsByName, parentProject.evaluatedProperties, ParentProject.ItemDefinitionLibrary) ); return(taskExecutedSuccessfully); }
/// <summary> /// Creates an instance of this class for the specified task. /// </summary> public TaskEngine ( XmlElement taskNodeXmlElement, ITaskHost hostObject, string projectFileOfTaskNode, string parentProjectFullFileName, EngineLoggingServices loggingServices, int handleId, TaskExecutionModule parentModule, BuildEventContext targetBuildEventContext ) { ErrorUtilities.VerifyThrow(taskNodeXmlElement != null, "Need to specify the task node."); ErrorUtilities.VerifyThrow(projectFileOfTaskNode != null, "Need to specify path of project."); ErrorUtilities.VerifyThrow(parentProjectFullFileName != null, "Need to specify name of project."); ErrorUtilities.VerifyThrow(loggingServices != null, "Need to specify the node logger."); this.taskNode = taskNodeXmlElement; this.taskClass = null; this.hostObject = hostObject; this.projectFileOfTaskNode = projectFileOfTaskNode; this.parentProjectFullFileName = parentProjectFullFileName; this.loggingServices = loggingServices; this.handleId = handleId; this.parentModule = parentModule; this.continueOnError = false; this.conditionAttribute = taskNode.Attributes[XMakeAttributes.condition]; this.buildEventContext = targetBuildEventContext; }
/// <summary> /// This constructor is used by the class internally to create new instances when a thread /// becomes blocked by a user code callback. /// </summary> private TaskWorkerThread ( TaskExecutionModule parentModule, ManualResetEvent exitTaskThreads, ExitTaskCache exitTaskThreadsCache, Queue<TaskWorkerThread> workerThreadQueue, Hashtable handleIdToWorkerThread, Queue<TaskExecutionState> workItemQueue, ManualResetEvent workItemInsertionEvent, Hashtable waitingTasks, bool profileExecution ) { this.parentModule = parentModule; this.exitTaskThreads = exitTaskThreads; this.exitTaskThreadsCache = exitTaskThreadsCache; this.workerThreadQueue = workerThreadQueue; this.handleIdToWorkerThread = handleIdToWorkerThread; this.workItemQueue = workItemQueue; this.workItemInsertionEvent = workItemInsertionEvent; this.waitingTasks = waitingTasks; this.profileExecution = profileExecution; InitializePerInstanceData(); }
/// <summary> /// This constructor creates a worker thread which is immediately ready to be activated. Once /// activated the thread will execute tasks as they appear in the work item queue. Once the /// thread is blocked from executing tasks it will pass the ownership of the work item queue to another /// thread /// </summary> internal TaskWorkerThread(TaskExecutionModule parentModule, bool profileExecution) { this.parentModule = parentModule; // Initialize the data that only has to be set by the very first thread // created by the TEM this.exitTaskThreads = new ManualResetEvent(false); this.exitTaskThreadsCache = new ExitTaskCache(false); this.workerThreadQueue = new Queue <TaskWorkerThread>(); this.handleIdToWorkerThread = new Hashtable(); this.workItemQueue = new Queue <TaskExecutionState>(); this.workItemInsertionEvent = new ManualResetEvent(false); this.waitingTasks = new Hashtable(); this.profileExecution = profileExecution; InitializePerInstanceData(); }
/// <summary> /// Default constructor. /// </summary> internal NodeManager(int cpuCount, bool childMode, Engine parentEngine) { nodeList = new List <ProvidersNodeInformation>(); nodeProviders = new List <INodeProvider>(); this.parentEngine = parentEngine; this.statusMessageReceived = new ManualResetEvent(false); // Create the inproc node, this means that there will always be one node, node 0 if (taskExecutionModule == null) { taskExecutionModule = new TaskExecutionModule(parentEngine.EngineCallback, (cpuCount == 1 && !childMode ? TaskExecutionModule.TaskExecutionModuleMode.SingleProcMode : TaskExecutionModule.TaskExecutionModuleMode.MultiProcFullNodeMode), parentEngine.ProfileBuild); } }
/// <summary> /// Default constructor. /// </summary> internal NodeManager(int cpuCount, bool childMode, Engine parentEngine) { nodeList = new List<ProvidersNodeInformation>(); nodeProviders = new List<INodeProvider>(); this.parentEngine = parentEngine; this.statusMessageReceived = new ManualResetEvent(false); // Create the inproc node, this means that there will always be one node, node 0 if (taskExecutionModule == null) { taskExecutionModule = new TaskExecutionModule(parentEngine.EngineCallback, (cpuCount == 1 && !childMode ? TaskExecutionModule.TaskExecutionModuleMode.SingleProcMode : TaskExecutionModule.TaskExecutionModuleMode.MultiProcFullNodeMode), parentEngine.ProfileBuild); } }
/// <summary> /// This constructor creates a worker thread which is immediately ready to be activated. Once /// activated the thread will execute tasks as they appear in the work item queue. Once the /// thread is blocked from executing tasks it will pass the ownership of the work item queue to another /// thread /// </summary> internal TaskWorkerThread(TaskExecutionModule parentModule, bool profileExecution) { this.parentModule = parentModule; // Initialize the data that only has to be set by the very first thread // created by the TEM this.exitTaskThreads = new ManualResetEvent(false); this.exitTaskThreadsCache = new ExitTaskCache(false); this.workerThreadQueue = new Queue<TaskWorkerThread>(); this.handleIdToWorkerThread = new Hashtable(); this.workItemQueue = new Queue<TaskExecutionState>(); this.workItemInsertionEvent = new ManualResetEvent(false); this.waitingTasks = new Hashtable(); this.profileExecution = profileExecution; InitializePerInstanceData(); }
/// <summary> /// Create an instance of this class to represent the IBuildEngine2 interface to the task /// including the event location where the log messages are raised /// </summary> /// <param name="parentModule">Parent Task Execution Module</param> /// <param name="handleId"></param> /// <param name="parentProjectFullFileName">the full path to the currently building project</param> /// <param name="projectFileOfTaskNode">the path to the actual file (project or targets) where the task invocation is located</param> /// <param name="loggingServices"></param> /// <param name="buildEventContext">Event Context where events will be seen to be raised from. Task messages will get this as their event context</param> internal EngineProxy ( TaskExecutionModule parentModule, int handleId, string parentProjectFullFileName, string projectFileOfTaskNode, EngineLoggingServices loggingServices, BuildEventContext buildEventContext ) { ErrorUtilities.VerifyThrow(parentModule != null, "No parent module."); ErrorUtilities.VerifyThrow(loggingServices != null, "No logging services."); ErrorUtilities.VerifyThrow(projectFileOfTaskNode != null, "Need project file path string"); this.parentModule = parentModule; this.handleId = handleId; this.parentProjectFullFileName = parentProjectFullFileName; this.projectFileOfTaskNode = projectFileOfTaskNode; this.loggingServices = loggingServices; this.buildEventContext = buildEventContext; this.callbackMonitor = new object(); activeProxy = true; }
public void TestTEMBatchSizeSettings() { Engine e = new Engine(@"C:\binpath"); EngineLoggingServicesHelper loggingServicesHelper = new EngineLoggingServicesHelper(); e.LoggingServices = loggingServicesHelper; EngineCallback engineCallback = new EngineCallback(e); Environment.SetEnvironmentVariable("MSBUILDREQUESTBATCHSIZE", "-4"); TaskExecutionModule TEM = new TaskExecutionModule(engineCallback, TaskExecutionModule.TaskExecutionModuleMode.MultiProcFullNodeMode, false); DualQueue<BuildEventArgs> currentQueue = loggingServicesHelper.GetCurrentQueueBuildEvents(); BuildEventArgs currentEvent = currentQueue.Dequeue(); Assertion.Assert("Expected event to be a warning event", currentEvent is BuildWarningEventArgs); Assertion.Assert(String.Compare(ResourceUtilities.FormatResourceString("BatchRequestSizeOutOfRange", "-4"), ((BuildWarningEventArgs)currentEvent).Message, StringComparison.OrdinalIgnoreCase) == 0); e = new Engine(@"C:\binpath"); loggingServicesHelper = new EngineLoggingServicesHelper(); e.LoggingServices = loggingServicesHelper; engineCallback = new EngineCallback(e); Environment.SetEnvironmentVariable("MSBUILDREQUESTBATCHSIZE", "0"); TEM = new TaskExecutionModule(engineCallback, TaskExecutionModule.TaskExecutionModuleMode.MultiProcFullNodeMode, false); currentQueue = loggingServicesHelper.GetCurrentQueueBuildEvents(); currentEvent = currentQueue.Dequeue(); Assertion.Assert("Expected event to be a warning event", currentEvent is BuildWarningEventArgs); Assertion.Assert(String.Compare(ResourceUtilities.FormatResourceString("BatchRequestSizeOutOfRange", "0"), ((BuildWarningEventArgs)currentEvent).Message, StringComparison.OrdinalIgnoreCase) == 0); e = new Engine(@"C:\binpath"); loggingServicesHelper = new EngineLoggingServicesHelper(); e.LoggingServices = loggingServicesHelper; engineCallback = new EngineCallback(e); Environment.SetEnvironmentVariable("MSBUILDREQUESTBATCHSIZE", int.MaxValue.ToString()); TEM = new TaskExecutionModule(engineCallback, TaskExecutionModule.TaskExecutionModuleMode.MultiProcFullNodeMode, false); currentQueue = loggingServicesHelper.GetCurrentQueueBuildEvents(); Assertion.Assert(currentQueue.Count == 0); e = new Engine(@"C:\binpath"); loggingServicesHelper = new EngineLoggingServicesHelper(); e.LoggingServices = loggingServicesHelper; engineCallback = new EngineCallback(e); Environment.SetEnvironmentVariable("MSBUILDREQUESTBATCHSIZE", "4"); TEM = new TaskExecutionModule(engineCallback, TaskExecutionModule.TaskExecutionModuleMode.MultiProcFullNodeMode, false); currentQueue = loggingServicesHelper.GetCurrentQueueBuildEvents(); Assertion.Assert(currentQueue.Count == 0); e = new Engine(@"C:\binpath"); loggingServicesHelper = new EngineLoggingServicesHelper(); e.LoggingServices = loggingServicesHelper; engineCallback = new EngineCallback(e); Environment.SetEnvironmentVariable("MSBUILDREQUESTBATCHSIZE", "Giberish"); TEM = new TaskExecutionModule(engineCallback, TaskExecutionModule.TaskExecutionModuleMode.MultiProcFullNodeMode, false); currentQueue = loggingServicesHelper.GetCurrentQueueBuildEvents(); currentEvent = currentQueue.Dequeue(); Assertion.Assert("Expected event to be a warning event", currentEvent is BuildWarningEventArgs); Assertion.Assert(String.Compare(ResourceUtilities.FormatResourceString("BatchRequestSizeOutOfRange", "Giberish"), ((BuildWarningEventArgs)currentEvent).Message, StringComparison.OrdinalIgnoreCase) == 0); }
public MockTaskExecutionModule(EngineCallback nodeProxy, TaskExecutionModule.TaskExecutionModuleMode moduleMode) : base(nodeProxy, moduleMode, false) { }
/// <summary> /// Creates an instance of a MockTask, and returns the objects necessary to exercise /// taskEngine.InitializeTask /// </summary> /// <param name="taskNode"></param> /// <param name="taskEngine"></param> /// <param name="mockTask"></param> /// <param name="itemBucket"></param> /// <owner>RGoel</owner> private void InstantiateMockTaskHelper ( XmlElement taskNode, out TaskEngine taskEngine, out MockTask mockTask, out ItemBucket itemBucket, out EngineProxy engineProxy, string condition ) { LoadedType taskClass = new LoadedType(typeof(MockTask), new AssemblyLoadInfo(typeof(MockTask).Assembly.FullName, null)); Engine engine = new Engine(@"c:\"); Project project = new Project(engine); EngineCallback engineCallback = new EngineCallback(engine); TaskExecutionModule taskExecutionModule = new TaskExecutionModule(engineCallback, TaskExecutionModule.TaskExecutionModuleMode.SingleProcMode, false); ProjectBuildState buildContext = new ProjectBuildState(null, null, new BuildEventContext(0, 1, 1, 1)); int nodeProxyID = engineCallback.CreateTaskContext(project, null, buildContext, taskNode, EngineCallback.inProcNode, new BuildEventContext(BuildEventContext.InvalidNodeId, BuildEventContext.InvalidTargetId, BuildEventContext.InvalidProjectContextId, BuildEventContext.InvalidTaskId)); taskEngine = new TaskEngine ( taskNode, null, /* host object */ "In Memory", project.FullFileName, engine.LoggingServices, nodeProxyID, taskExecutionModule, new BuildEventContext(0, 1, 1, 1) ); taskEngine.TaskClass = taskClass; engineProxy = new EngineProxy(taskExecutionModule, nodeProxyID, project.FullFileName, project.FullFileName, engine.LoggingServices, null); mockTask = new MockTask(new EngineProxy(taskExecutionModule, nodeProxyID, project.FullFileName, project.FullFileName, engine.LoggingServices, null)); // The code below creates an item table that is equivalent to the following MSBuild syntax: // // <ItemGroup> // <ItemListContainingOneItem Include="a.cs"> // <Culture>fr-fr</Culture> // </ItemListContainingOneItem> // // <ItemListContainingTwoItems Include="b.cs"> // <HintPath>c:\foo</HintPath> // </ItemListContainingTwoItems> // <ItemListContainingTwoItems Include="c.cs"> // <HintPath>c:\bar</HintPath> // </ItemListContainingTwoItems> // </ItemGroup> // Hashtable itemsByName = new Hashtable(StringComparer.OrdinalIgnoreCase); BuildItemGroup itemListContainingOneItem = new BuildItemGroup(); BuildItem a = itemListContainingOneItem.AddNewItem("ItemListContainingOneItem", "a.cs"); a.SetMetadata("Culture", "fr-fr"); itemsByName["ItemListContainingOneItem"] = itemListContainingOneItem; BuildItemGroup itemListContainingTwoItems = new BuildItemGroup(); BuildItem b = itemListContainingTwoItems.AddNewItem("ItemListContainingTwoItems", "b.cs"); b.SetMetadata("HintPath", "c:\\foo"); BuildItem c = itemListContainingTwoItems.AddNewItem("ItemListContainingTwoItems", "c.cs"); c.SetMetadata("HintPath", "c:\\bar"); itemsByName["ItemListContainingTwoItems"] = itemListContainingTwoItems; itemBucket = new ItemBucket(new string[0], new Dictionary<string, string>(), LookupHelpers.CreateLookup(itemsByName), 0); }
/// <summary> /// Indicates to the EngineProxy that it is no longer needed. /// Called by TaskEngine when the task using the EngineProxy is done. /// </summary> internal void MarkAsInActive() { activeProxy = false; // Since the task has a pointer to this class it may store it in a static field. Null out // internal data so the leak of this object doesn't lead to a major memory leak. loggingServices = null; parentModule = null; buildEventContext = null; // Clear out the sponsor (who is responsible for keeping the EngineProxy remoting lease alive until the task is done) // this will be null if the engineproxy was never sent accross an appdomain boundry. if (sponsor != null) { ILease lease = (ILease)RemotingServices.GetLifetimeService(this); if (lease != null) { lease.Unregister(sponsor); } sponsor.Close(); sponsor = null; } }
/// <summary> /// Create an instance of this class to represent the IBuildEngine2 interface to the task /// including the event location where the log messages are raised /// </summary> /// <param name="parentModule">Parent Task Execution Module</param> /// <param name="handleId"></param> /// <param name="parentProjectFullFileName">the full path to the currently building project</param> /// <param name="projectFileOfTaskNode">the path to the actual file (project or targets) where the task invocation is located</param> /// <param name="loggingServices"></param> /// <param name="buildEventContext">Event Context where events will be seen to be raised from. Task messages will get this as their event context</param> internal EngineProxy ( TaskExecutionModule parentModule, int handleId, string parentProjectFullFileName, string projectFileOfTaskNode, EngineLoggingServices loggingServices, BuildEventContext buildEventContext ) { ErrorUtilities.VerifyThrow(parentModule != null, "No parent module."); ErrorUtilities.VerifyThrow(loggingServices != null, "No logging services."); ErrorUtilities.VerifyThrow(projectFileOfTaskNode != null, "Need project file path string"); this.parentModule = parentModule; this.handleId = handleId; this.parentProjectFullFileName = parentProjectFullFileName; this.projectFileOfTaskNode = projectFileOfTaskNode; this.loggingServices = loggingServices; this.buildEventContext = buildEventContext; this.callbackMonitor = new object(); activeProxy = true; }
public void TaskWorkerThreadTest() { // This event will be triggered right before a "engine" call back is made. // Once this event is fired we insert another item into the queue ManualResetEvent rightBeforeCallbackBlock = new ManualResetEvent(false); engine = new Engine(@"c:\"); TaskExecutionModule TEM = new TaskExecutionModule(new EngineCallback(engine), TaskExecutionModule.TaskExecutionModuleMode.MultiProcFullNodeMode, false); // Create a worker thread and make it the active node thread TaskWorkerThread workerThread = TEM.GetWorkerThread(); // Get some tasks which we can then provide execution methods to List<TaskExecutionStateHelper> tasks = InitializeTaskState(); tasks[1].ExecutionTaskDelegateParameter = rightBeforeCallbackBlock; tasks[1].ExecuteDelegate = delegate(object parameter) { ((ManualResetEvent)parameter).Set(); workerThread.WaitForResults(tasks[1].HandleId, new BuildResult[] { new BuildResult(null, new Hashtable(StringComparer.OrdinalIgnoreCase), true, tasks[1].HandleId, 0, 2, false, string.Empty, string.Empty, 0, 0, 0) }, new BuildRequest[1]); }; // Task 0 will cause a baseActiveThread to start up and run workerThread.PostWorkItem(tasks[0]); // Since this will do a callback and will generate a waitingActiveThread workerThread.PostWorkItem(tasks[1]); workerThread.ActivateThread(); // Wait for the call back to happen rightBeforeCallbackBlock.WaitOne(); // Lets insert a execution task which and post a work item which will cause a localDoneEvent to be set tasks[2].ExecutionTaskDelegateParameter = null; tasks[2].ExecuteDelegate = null; // TaskWorkerThread.PostBuildResult(new BuildResult(null, true, tasks[2].NodeProxyId, 0)); workerThread.PostWorkItem(tasks[2]); //Post a build Result while one of the threads is waiting active, this should cause us to reuse the first thread workerThread.PostBuildResult(new BuildResult(null, new Hashtable(StringComparer.OrdinalIgnoreCase), true, tasks[2].HandleId, 0, 2, false, string.Empty, string.Empty, 0, 0, 0)); tasks[3].ExecutionTaskDelegateParameter = null; tasks[3].ExecuteDelegate = null; workerThread.PostWorkItem(tasks[3]); TEM.Shutdown(); // Count up the number of threads used during the execution of the tasks List<int> threadsUsedForExecution = new List<int>(); foreach (TaskExecutionStateHelper state in tasks) { // If the list does not contain the threadId add it to the list if (!threadsUsedForExecution.Contains(state.ThreadId)) { threadsUsedForExecution.Add(state.ThreadId); } } // Make sure we use less threads then the number of sumbitted tasks which would indicate that threads are reused Assert.IsTrue(threadsUsedForExecution.Count < tasks.Count, "Expected for the number of unique threads to be less than the number of tasks as threads should have been reused"); }