1 // Copyright (c) Microsoft. All rights reserved. 2 // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 4 using System; 5 using System.Collections; 6 using System.Collections.Generic; 7 using System.IO; 8 using System.Text; 9 using System.Threading; 10 11 using Microsoft.Build.Framework; 12 using Microsoft.Build.BuildEngine.Shared; 13 14 namespace Microsoft.Build.BuildEngine 15 { 16 /// <summary> 17 /// This class is responsible for representing the task execution subsystem to the engine. This 18 /// class can be instantiated in a different appdomain from the engine and doesn't share 19 /// any pointers/data with the engine(except for the argument to the functions). 20 /// </summary> 21 internal class TaskExecutionModule 22 { 23 #region Constructors 24 25 /// <summary> 26 /// The TaskExecutionModule is a the external view into a subsystem responsible for executing user 27 /// tasks. The subsystem consists of TaskWorkerThread, TaskEngine, TaskExecutionState and EngineProxy. 28 /// The engine thread passes the TaskExecutionState to the TEM which after the task finishes passes the 29 /// results back via the engineCallback. 30 /// </summary> TaskExecutionModule( EngineCallback engineCallback, TaskExecutionModuleMode moduleMode, bool profileExecution )31 internal TaskExecutionModule 32 ( 33 EngineCallback engineCallback, 34 TaskExecutionModuleMode moduleMode, 35 bool profileExecution 36 ) 37 { 38 this.engineCallback = engineCallback; 39 this.moduleMode = moduleMode; 40 // By default start in breadthFirst traversal. This is done to gather enough work at the start of the build to get all the nodes at least working on something. 41 this.breadthFirstTraversal = true; 42 this.profileExecution = profileExecution; 43 this.totalTaskTime = 0; 44 // Get the node the TEM is running on, this is so the parent engine knows which node is requesting a traversal strategy change. 45 nodeId = engineCallback.GetParentEngine().NodeId; 46 47 SetBatchRequestSize(); 48 49 // In singleproc mode the task execution module executes tasks on the engine thread. In multi proc mode a new thread is 50 // created so the TEM can submit tasks to a worker queue which will run the tasks on a new thread. 51 if (moduleMode != TaskExecutionModuleMode.SingleProcMode) 52 { 53 this.isRunningMultipleNodes = true; 54 this.activeThreadCount = 0; 55 this.overallThreadCount = 0; 56 this.threadActiveCountEvent = new ManualResetEvent(false); 57 this.threadOverallCountEvent = new ManualResetEvent(false); 58 this.lastTaskActivity = 0; 59 60 // Create a worker thread and make it the active node thread 61 workerThread = new TaskWorkerThread(this, profileExecution); 62 workerThread.ActivateThread(); 63 } 64 else 65 { 66 this.isRunningMultipleNodes = false; 67 } 68 } 69 70 /// <summary> 71 /// Sets the requestBatch size based on an environment variable set by the user. 72 /// </summary> SetBatchRequestSize()73 private void SetBatchRequestSize() 74 { 75 // The RequestBatchSize is how many buildrequests will be sent to the parent engine at once when the system 76 // is running in multiproc mode. The idea of only sending a certain ammount of build requests was implemented 77 // due to the parent engine being flooded with build requests if the tree being built is very broad. 78 string requestBatchSizeEnvironmentVariable = Environment.GetEnvironmentVariable("MSBUILDREQUESTBATCHSIZE"); 79 if (!String.IsNullOrEmpty(requestBatchSizeEnvironmentVariable)) 80 { 81 if (!int.TryParse(requestBatchSizeEnvironmentVariable, out batchRequestSize) || batchRequestSize < 1) 82 { 83 // If an invalid RequestBatchSize is passed in set the batchRequestSize back to the default and log a warning. 84 batchRequestSize = defaultBatchRequestSize; 85 BuildEventContext buildEventContext = new BuildEventContext( 86 nodeId, 87 BuildEventContext.InvalidTargetId, 88 BuildEventContext.InvalidProjectContextId, 89 BuildEventContext.InvalidTaskId 90 ); 91 92 engineCallback.GetParentEngine().LoggingServices.LogWarning(buildEventContext, new BuildEventFileInfo(/* there is truly no file associated with this warning */ String.Empty), "BatchRequestSizeOutOfRange", requestBatchSizeEnvironmentVariable); 93 } 94 } 95 } 96 97 #endregion 98 99 #region Properties 100 101 /// <summary> 102 /// This property allows a task to query whether or not the system is running in single process mode or multi process mode. 103 /// Single process mode (IsRunningMultipleNodes = false) is where the engine is initialized with the number of cpus = 1 and the engine is not a child engine. 104 /// The engine is in multi process mode (IsRunningMultipleNodes = true) when the engine is initialized with a number of cpus > 1 or the engine is a child engine. 105 /// </summary> 106 internal bool IsRunningMultipleNodes 107 { 108 get 109 { 110 return isRunningMultipleNodes; 111 } 112 } 113 114 /// <summary> 115 /// Specifies the traversal type for IBuildEngine callbacks. If true multiple build requests will be sent 116 /// to the engine if false the requests will be sent one at a time. 117 /// </summary> 118 internal bool UseBreadthFirstTraversal 119 { 120 get 121 { 122 return breadthFirstTraversal; 123 } 124 set 125 { 126 breadthFirstTraversal = value; 127 } 128 } 129 130 /// <summary> 131 /// Returns true if the TEM doesn't have a thread in user code and there are no pending 132 /// workitems 133 /// </summary> 134 internal bool IsIdle 135 { 136 get 137 { 138 return (activeThreadCount == 0 && workerThread.WorkItemCount == 0); 139 } 140 } 141 142 /// <summary> 143 /// Return total time spent executing the tasks by this TEM. This value is only valid if the TEM is created with 144 /// profileExecution set to true, otherwise this value will be 0 145 /// </summary> 146 internal long TaskExecutionTime 147 { 148 get 149 { 150 return totalTaskTime; 151 } 152 } 153 154 #endregion 155 #region Method used internally inside the TEM boundary (i.e. not called from the engine) 156 157 /// <summary> 158 /// This method passes the task outputs to the engine, it is virtual for testing purposes to 159 /// create a mock TEM 160 /// </summary> PostTaskOutputs( int handleId, bool taskExecutedSuccessfully, Exception thrownException, long executionTime )161 virtual internal void PostTaskOutputs 162 ( 163 int handleId, 164 bool taskExecutedSuccessfully, 165 Exception thrownException, 166 long executionTime 167 ) 168 { 169 totalTaskTime += executionTime; 170 engineCallback.PostTaskOutputs(handleId, taskExecutedSuccessfully, thrownException, executionTime); 171 } 172 173 /// <summary> 174 /// This function implements the callback via the IBuildEngine interface 175 /// </summary> 176 /// <returns>result of call to engine</returns> BuildProjectFile( int handleId, string[] projectFileNames, string[] targetNames, IDictionary[] globalPropertiesPerProject, IDictionary[] targetOutputsPerProject, EngineLoggingServices loggingServices, string [] toolsVersions, bool useResultsCache, bool unloadProjectsOnCompletion, BuildEventContext taskContext )177 virtual internal bool BuildProjectFile 178 ( 179 int handleId, 180 string[] projectFileNames, 181 string[] targetNames, 182 IDictionary[] globalPropertiesPerProject, 183 IDictionary[] targetOutputsPerProject, 184 EngineLoggingServices loggingServices, 185 string [] toolsVersions, 186 bool useResultsCache, 187 bool unloadProjectsOnCompletion, 188 BuildEventContext taskContext 189 ) 190 { 191 if (projectFileNames.Length == 0) 192 { 193 // Nothing to do, just return success 194 return true; 195 } 196 197 string currentDir = FileUtilities.GetCurrentDirectoryStaticBuffer(currentDirectoryBuffer); 198 199 if (Engine.debugMode) 200 { 201 string targetName = targetNames == null ? "null" : targetNames[0]; 202 203 bool remoteNode = false; 204 for (int r = 0; r < projectFileNames.Length; r++) 205 { 206 string fullProjectName = projectFileNames[r] != null ? 207 projectFileNames[r] : "null"; 208 Console.WriteLine("RemoteNode: " + remoteNode + " Project " + fullProjectName + " T:" + targetName + " NodeProdyId# " + handleId + " Time " + DateTime.Now.ToLongTimeString()); 209 if (globalPropertiesPerProject[r] != null) 210 { 211 foreach (DictionaryEntry entry in globalPropertiesPerProject[r]) 212 { 213 Console.WriteLine(currentDir + " :GLOBAL " + entry.Key + "=" + entry.Value.ToString()); 214 } 215 } 216 } 217 } 218 219 BuildRequest[] buildRequests = new BuildRequest[projectFileNames.Length]; 220 for (int i = 0; i < buildRequests.Length; i++) 221 { 222 // We need to get the full path to the project before we call back 223 // into the engine which has no control over current path 224 string fullProjectName = projectFileNames[i] != null ? 225 Path.GetFullPath(projectFileNames[i]) : null; 226 227 buildRequests[i] = new BuildRequest(handleId, fullProjectName, targetNames, globalPropertiesPerProject[i], 228 toolsVersions[i], i, useResultsCache, unloadProjectsOnCompletion); 229 ErrorUtilities.VerifyThrow(buildRequests[i].IsGeneratedRequest == true, "Should not be sending non generated requests from TEM to engine"); 230 buildRequests[i].ParentBuildEventContext = taskContext; 231 } 232 233 BuildResult[] buildResultsLocal = new BuildResult[projectFileNames.Length]; 234 235 if (moduleMode == TaskExecutionModuleMode.SingleProcMode) 236 { 237 for (int i = 0; i < projectFileNames.Length; i++) 238 { 239 // If we are running in a single threaded mode we need to 240 // re-enter the main build loop on the current thread in order 241 // to build the requested project, because the main build is below 242 // us on the stack 243 engineCallback.PostBuildRequestsToHost(new BuildRequest[] { buildRequests[i] }); 244 buildResultsLocal[i] = engineCallback.GetParentEngine().EngineBuildLoop(buildRequests[i]); 245 buildResultsLocal[i].ConvertToTaskItems(); 246 } 247 } 248 else 249 { 250 WaitForBuildResults(handleId, buildResultsLocal, buildRequests); 251 } 252 253 // Store the outputs in the hashtables provided by the caller 254 bool overallResult = true; 255 for (int i = 0; i < buildResultsLocal.Length; i++) 256 { 257 // Users of the Object Model can pass in null targetOutputs for projects they do not want outputs for 258 // therefore we need to make sure that there are targetoutputs and the users want the results 259 if (buildResultsLocal[i] != null) 260 { 261 if (buildResultsLocal[i].OutputsByTarget != null && targetOutputsPerProject[i] != null) 262 { 263 foreach (DictionaryEntry entry in buildResultsLocal[i].OutputsByTarget) 264 { 265 targetOutputsPerProject[i].Add(entry.Key, entry.Value); 266 } 267 overallResult = overallResult && buildResultsLocal[i].EvaluationResult; 268 } 269 } 270 else 271 { 272 // The calculation was terminated prior to receiving the result 273 overallResult = false; 274 } 275 } 276 277 // We're now returning from an IBuildEngine callback; 278 // set the current directory back to what the tasks expect 279 if (Directory.GetCurrentDirectory() != currentDir) 280 { 281 Directory.SetCurrentDirectory(currentDir); 282 } 283 284 if (Engine.debugMode) 285 { 286 bool remoteNode = false; 287 string targetName = targetNames == null ? "null" : targetNames[0]; 288 Console.WriteLine("RemoteNode: " + remoteNode + " T:" + targetName + " HandleId# " + handleId + " Result " + overallResult); 289 } 290 291 return overallResult; 292 } 293 294 /// <summary> 295 /// Once the buildRequests from the EngineCallback have been created they are sent to this method which will 296 /// post the build requests to the parent engine and then wait on the results to come back. 297 /// This method uses either a breadthFirst or depthFirst traversal strategy when sending buildRequests to the parent engine. 298 /// This method will start in breadthFirst traversal. It will continue to use this strategy until one of two events occur: 299 /// 1. The parent node sents a message indicating the TEM should switch to depthFirst traversal. 300 /// 2. The number of buildRequests is larger than the batchRequestSize. 301 /// In both of these cases the system will go from a breadthFirstTraversal to a depthFirst Traversal. In the second case 302 /// a message will be sent to the parent engine to switch the system to depthFirst traversal as the system is starting to 303 /// be overloaded with work. 304 /// In a depth first strategy the buildRequests will be sent to the parent engine one at a time and waiting for results for 305 /// each buildRequest sent. In a breadthFirst traversal strategy some number of the buildrequests will be sent to the parent engine 306 /// in a batch of requests. The system will then wait on the results of ALL the build requests sent before continuing 307 /// to send more build requests. 308 /// </summary> WaitForBuildResults(int handleId, BuildResult[] buildResultsLocal, BuildRequest[] buildRequests)309 private void WaitForBuildResults(int handleId, BuildResult[] buildResultsLocal, BuildRequest[] buildRequests) 310 { 311 // If the traversal strategy is breadth first and the number of requests is less than the batchRequestSize 312 // or if there is only 1 build request then send ALL build requests to the parent engine and wait on the results. 313 if ((breadthFirstTraversal == true && buildRequests.Length < batchRequestSize) || buildRequests.Length == 1) 314 { 315 engineCallback.PostBuildRequestsToHost(buildRequests); 316 workerThread.WaitForResults(handleId, buildResultsLocal, buildRequests); 317 } 318 else 319 { 320 int currentRequestIndex = 0; // Which build request is being processed 321 int numberOfRequestsToSend = 0; // How many buildRequests are going to be sent based on the number of buildRequests remaining and the build request batch size. 322 323 // Arrays that will be used to partion the buildRequests array when sending batches of builds requests at a time. 324 BuildRequest[] wrapperArrayBreadthFirst = new BuildRequest[batchRequestSize]; 325 BuildResult[] resultsArrayBreadthFirst = new BuildResult[batchRequestSize]; 326 327 // Pre allocate these arrays as they will always be only one element in size. They are assigned to and filled when doing a depth first traversal. 328 BuildRequest[] wrapperArrayDepthFirst = new BuildRequest[1]; 329 BuildResult[] resultsArrayDepthFirst = new BuildResult[1]; 330 331 // While there are still requests to send 332 while (currentRequestIndex < buildRequests.Length) 333 { 334 // If there is a breadth first traversal and there are more than batchRequestSize build requests, send the first batchRequestSize, then do the rest depth first 335 if (breadthFirstTraversal == true) 336 { 337 // Figure out how many requests to send, either the full batch size or only part of a batch 338 numberOfRequestsToSend = (buildRequests.Length - currentRequestIndex) <batchRequestSize ? (buildRequests.Length - currentRequestIndex) : batchRequestSize; 339 340 // Initialize the wrapper array to how many requests are going to be sent 341 if (numberOfRequestsToSend != wrapperArrayBreadthFirst.Length) 342 { 343 wrapperArrayBreadthFirst = new BuildRequest[numberOfRequestsToSend]; 344 resultsArrayBreadthFirst = new BuildResult[numberOfRequestsToSend]; 345 } 346 347 // Fill the wrapper array with one batch of build requests 348 for (int i = 0; i < numberOfRequestsToSend; i++) 349 { 350 wrapperArrayBreadthFirst[i] = buildRequests[currentRequestIndex + i]; 351 wrapperArrayBreadthFirst[i].RequestId = i; 352 resultsArrayBreadthFirst[i] = null; 353 } 354 355 engineCallback.PostBuildRequestsToHost(wrapperArrayBreadthFirst); 356 357 // Only switch from breadth to depth if there are more thanbatchRequestSize items 358 if ((buildRequests.Length - currentRequestIndex) > batchRequestSize) 359 { 360 engineCallback.PostStatus(nodeId, new NodeStatus(false /* use depth first traversal*/), false /* don't block waiting on the send */); 361 breadthFirstTraversal = false; 362 } 363 364 workerThread.WaitForResults(handleId, resultsArrayBreadthFirst, wrapperArrayBreadthFirst); 365 Array.Copy(resultsArrayBreadthFirst, 0, buildResultsLocal, currentRequestIndex, numberOfRequestsToSend); 366 currentRequestIndex += numberOfRequestsToSend; 367 } 368 369 // Proceed with depth first traversal 370 while ((currentRequestIndex < buildRequests.Length) && !breadthFirstTraversal) 371 { 372 wrapperArrayDepthFirst[0] = buildRequests[currentRequestIndex]; 373 buildRequests[currentRequestIndex].RequestId = 0; 374 resultsArrayDepthFirst[0] = null; 375 376 engineCallback.PostBuildRequestsToHost(wrapperArrayDepthFirst); 377 workerThread.WaitForResults(handleId, resultsArrayDepthFirst, wrapperArrayDepthFirst); 378 //Copy the result from an intermediate array to the full array 379 buildResultsLocal[currentRequestIndex] = resultsArrayDepthFirst[0]; 380 //Check if the call failed (null result was returned) 381 if (buildResultsLocal[currentRequestIndex] == null) 382 { 383 return; 384 } 385 386 //Move to the next request 387 currentRequestIndex++; 388 } 389 } 390 } 391 } 392 393 /// <summary> 394 /// Call into the engine to figure out the line and column number of the task XML node in the original 395 /// project context 396 /// </summary> GetLineColumnOfXmlNode(int handleId, out int lineNumber, out int columnNumber)397 virtual internal void GetLineColumnOfXmlNode(int handleId, out int lineNumber, out int columnNumber) 398 { 399 engineCallback.GetLineColumnOfXmlNode(handleId, out lineNumber, out columnNumber); 400 } 401 402 /// <summary> 403 /// Gets the global tasks registry defined by the *.tasks files. 404 /// </summary> 405 /// <returns>Global/default tasks registry.</returns> GetDefaultTasksRegistry(int handleId)406 internal ITaskRegistry GetDefaultTasksRegistry(int handleId) 407 { 408 return engineCallback.GetEngineTaskRegistry(handleId); 409 } 410 411 /// <summary> 412 /// Gets the tasks registry for the given project. 413 /// </summary> 414 /// <returns>Project task registry.</returns> GetProjectTasksRegistry(int handleId)415 internal ITaskRegistry GetProjectTasksRegistry(int handleId) 416 { 417 return engineCallback.GetProjectTaskRegistry(handleId); 418 } 419 420 /// <summary> 421 /// Gets the path to the tools used for the particular task 422 /// </summary> GetToolsPath(int handleId)423 internal string GetToolsPath(int handleId) 424 { 425 return engineCallback.GetToolsPath(handleId); 426 } 427 RethrowTaskExceptions()428 internal bool RethrowTaskExceptions() 429 { 430 return (moduleMode == TaskExecutionModuleMode.SingleProcMode); 431 } 432 433 #endregion 434 435 #region Methods called from the engine 436 /// <summary> 437 /// Called to execute a task within a target. This method instantiates the task, sets its parameters, 438 /// and executes it. 439 /// </summary> 440 /// <param name="taskState"></param> ExecuteTask(TaskExecutionState taskState)441 public void ExecuteTask(TaskExecutionState taskState) 442 { 443 // Fill out the local fields of the task state 444 taskState.ParentModule = this; 445 taskState.LoggingService = engineCallback.GetParentEngine().LoggingServices; 446 taskState.ProfileExecution = profileExecution; 447 448 // If we running in single proc mode, we should execute this task on the current thread 449 if (moduleMode == TaskExecutionModuleMode.SingleProcMode) 450 { 451 taskState.ExecuteTask(); 452 } 453 else 454 { 455 // In multiproc mode post the work item to the workerThread queue so it can be executed by the worker thread. 456 workerThread.PostWorkItem(taskState); 457 } 458 } 459 460 /// <summary> 461 /// Uses the parent engine to get the next unique TaskID. 462 /// </summary> 463 /// <returns></returns> GetNextTaskId()464 internal int GetNextTaskId() 465 { 466 return engineCallback.GetParentEngine().GetNextTaskId(); 467 } 468 469 /// <summary> 470 /// This method lets the engine provide node with results of an evaluation it was waiting on. 471 /// </summary> 472 /// <param name="buildResult"></param> PostBuildResults(BuildResult buildResult)473 internal void PostBuildResults(BuildResult buildResult) 474 { 475 // Do nothing in the single proc mode 476 if (moduleMode == TaskExecutionModuleMode.SingleProcMode) 477 { 478 return; 479 } 480 481 buildResult.ConvertToTaskItems(); 482 workerThread.PostBuildResult(buildResult); 483 } 484 485 /// <summary> 486 /// This function returns the last time TEM was active executing a task 487 /// </summary> LastTaskActivity()488 internal long LastTaskActivity() 489 { 490 if (moduleMode != TaskExecutionModuleMode.SingleProcMode) 491 { 492 if (activeThreadCount == 0 && workerThread.WorkItemCount == 0) 493 { 494 return lastTaskActivity; 495 } 496 else 497 { 498 return DateTime.Now.Ticks; 499 } 500 } 501 return DateTime.Now.Ticks; 502 } 503 GetWaitingTaskData(List<BuildRequest []> outstandingRequests)504 internal int[] GetWaitingTaskData(List<BuildRequest []> outstandingRequests) 505 { 506 if (moduleMode != TaskExecutionModuleMode.SingleProcMode) 507 { 508 return workerThread.GetWaitingTasksData(outstandingRequests); 509 } 510 return new int [0]; 511 } 512 Shutdown()513 internal void Shutdown() 514 { 515 if (moduleMode != TaskExecutionModuleMode.SingleProcMode) 516 { 517 workerThread.Shutdown(); 518 519 while (overallThreadCount != 0) 520 { 521 threadOverallCountEvent.WaitOne(); 522 threadOverallCountEvent.Reset(); 523 } 524 525 if (profileExecution) 526 { 527 int taskTimeMs = 0; 528 529 if (totalTaskTime != 0) 530 { 531 TimeSpan taskTimeSpan = new TimeSpan(totalTaskTime); 532 taskTimeMs = (int)taskTimeSpan.TotalMilliseconds; 533 } 534 Console.WriteLine("Node time spent " + taskTimeMs); 535 } 536 } 537 } 538 #endregion 539 540 #region Methods used by worker threads 541 IncrementOverallThreadCount()542 internal void IncrementOverallThreadCount() 543 { 544 Interlocked.Increment(ref overallThreadCount); 545 } 546 DecrementOverallThreadCount()547 internal void DecrementOverallThreadCount() 548 { 549 Interlocked.Decrement(ref overallThreadCount); 550 threadOverallCountEvent.Set(); 551 } 552 DecrementActiveThreadCount()553 internal void DecrementActiveThreadCount() 554 { 555 Interlocked.Decrement(ref activeThreadCount); 556 threadActiveCountEvent.Set(); 557 } 558 WaitForZeroActiveThreadCount()559 internal void WaitForZeroActiveThreadCount() 560 { 561 while (Interlocked.CompareExchange(ref activeThreadCount, 1, 0) != 0) 562 { 563 threadActiveCountEvent.WaitOne(); 564 threadActiveCountEvent.Reset(); 565 } 566 lastTaskActivity = DateTime.Now.Ticks; 567 } 568 569 #endregion 570 571 #region Methods used for unittest only 572 /// <summary> 573 /// ONLY for unit testing 574 /// </summary> GetExecutionModuleMode()575 internal TaskExecutionModuleMode GetExecutionModuleMode() 576 { 577 // The Execution module mode is used to determine if they system is running under single proc or multiproc for the purposes of creating a new thread 578 // to execute tasks on. 579 return moduleMode; 580 } 581 582 /// <summary> 583 /// ONLY for unit testing 584 /// </summary> GetWorkerThread()585 internal TaskWorkerThread GetWorkerThread() 586 { 587 return workerThread; 588 } 589 #endregion 590 591 #region Member data 592 /// <summary> 593 /// Callback interface to communicate with the engine 594 /// </summary> 595 private EngineCallback engineCallback; 596 /// <summary> 597 /// The mode in which the TEM is running 598 /// </summary> 599 TaskExecutionModuleMode moduleMode; 600 /// <summary> 601 /// The class used to execute user tasks. 602 /// </summary> 603 TaskWorkerThread workerThread; 604 605 // Data shared between all worker threads within the TEM 606 /// <summary> 607 /// Total count of worker threads both active and inactive 608 /// </summary> 609 private int overallThreadCount; 610 /// <summary> 611 /// Event indicated a decrease in overallThreadCount due to an exit of a thread 612 /// </summary> 613 private ManualResetEvent threadOverallCountEvent; 614 /// <summary> 615 /// Count of active thread (i.e. threads in user code). Has to be either 0 or 1 616 /// </summary> 617 private int activeThreadCount; 618 /// <summary> 619 /// Event indicating a decrease in activeThreadCount due to a thread leaving user code 620 /// </summary> 621 private ManualResetEvent threadActiveCountEvent; 622 /// <summary> 623 /// Time stamp of last execution of user code. Only valid if activeThreadCount == 0. 624 /// </summary> 625 private long lastTaskActivity; 626 /// <summary> 627 /// Specifies the traversal type for callbacks. If true multiple build requests will be sent 628 /// to the engine if false the requests will be sent one at a time. 629 /// </summary> 630 private bool breadthFirstTraversal; 631 /// <summary> 632 /// Specifies if the timing data on task execution should be collected 633 /// </summary> 634 private bool profileExecution; 635 /// <summary> 636 /// Total time spent executing task code. Only valid if profileExecution is true. 637 /// </summary> 638 private long totalTaskTime; 639 640 /// <summary> 641 /// This property allows a task to query whether or not the system is running in single process mode or multi process mode. 642 /// Single process mode (IsRunningMultipleNodes = false) is where the engine is initialized with the number of cpus = 1 and the engine is not a child engine. 643 /// The engine is in multi process mode (IsRunningMultipleNodes = true) when the engine is initialized with a number of cpus > 1 or the engine is a child engine. 644 /// </summary> 645 private bool isRunningMultipleNodes; 646 647 private static StringBuilder currentDirectoryBuffer = new StringBuilder(270); 648 649 /// <summary> 650 /// In a multiproc build this is the maximum number of build requests which will be sent at a time to the parent engine 651 /// A default of 10 was an arbitrary number but turned out to be a good balance between being too small 652 /// causing the system to run out of work too quickly and being too big and flooding the system with requests. 653 /// </summary> 654 private const int defaultBatchRequestSize = 10; 655 private int batchRequestSize = defaultBatchRequestSize; 656 657 /// <summary> 658 /// The nodeId of the node the TaskExecutionModule is running on 659 /// </summary> 660 private int nodeId = -1; 661 662 #endregion 663 664 #region Enums 665 666 internal enum TaskExecutionModuleMode 667 { 668 /// <summary> 669 /// In this mode the tasks should be executed on the calling thread 670 /// </summary> 671 SingleProcMode = 0, 672 /// <summary> 673 /// In this mode the tasks should be executed on a different thread and the execute calls 674 /// should return immediately. The messages due to the task are not flushed. 675 /// </summary> 676 MultiProcFullNodeMode = 1, 677 } 678 679 #endregion 680 } 681 } 682