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.Generic; 6 7 using Microsoft.Build.Framework; 8 using Microsoft.Build.BuildEngine.Shared; 9 using System.Threading; 10 11 namespace Microsoft.Build.BuildEngine 12 { 13 /// <summary> 14 /// This class is resposible for managing the node providers - starting, stopping and sharing them. 15 /// Although child nodes have a NodeManager, theirs do nothing as they never has any NodeProviders registered with them. 16 /// </summary> 17 internal class NodeManager 18 { 19 #region Constructors 20 /// <summary> 21 /// Default constructor. 22 /// </summary> NodeManager(int cpuCount, bool childMode, Engine parentEngine)23 internal NodeManager(int cpuCount, bool childMode, Engine parentEngine) 24 { 25 nodeList = new List<ProvidersNodeInformation>(); 26 nodeProviders = new List<INodeProvider>(); 27 28 this.parentEngine = parentEngine; 29 30 this.statusMessageReceived = new ManualResetEvent(false); 31 32 // Create the inproc node, this means that there will always be one node, node 0 33 if (taskExecutionModule == null) 34 { 35 taskExecutionModule = new TaskExecutionModule(parentEngine.EngineCallback, 36 (cpuCount == 1 && !childMode ? TaskExecutionModule.TaskExecutionModuleMode.SingleProcMode : 37 TaskExecutionModule.TaskExecutionModuleMode.MultiProcFullNodeMode), parentEngine.ProfileBuild); 38 } 39 } 40 #endregion 41 42 #region Methods 43 44 /// <summary> 45 /// Register an instantiated INodeProvider with the node manager. The node manager will query the nodeprovider 46 /// for a list of its node descriptions, and add these nodes to a master list of nodes which can be used 47 /// by the scheduler. QUESTION: Do we allow duplicate Node Providers? 48 /// </summary> 49 /// <param name="providerToRegister"></param> 50 /// <returns></returns> RegisterNodeProvider(INodeProvider nodeProviderToRegister)51 internal bool RegisterNodeProvider(INodeProvider nodeProviderToRegister) 52 { 53 ErrorUtilities.VerifyThrowArgumentNull(nodeProviderToRegister,"nodeProviderToRegister"); 54 55 INodeDescription[] nodeDescriptions = nodeProviderToRegister.QueryNodeDescriptions(); 56 57 int[] nodeIds = new int[nodeDescriptions.Length]; 58 for (int i = 0; i < nodeIds.Length; i++) 59 { 60 nodeIds[i] = parentEngine.GetNextNodeId(); 61 } 62 nodeProviderToRegister.AssignNodeIdentifiers(nodeIds); 63 64 // Go through all of the nodes as described by nodeDescriptions and add them to out list of nodes 65 for(int i=0; i < nodeDescriptions.Length;i++) 66 { 67 ProvidersNodeInformation nodeToAddFromProvider = 68 new ProvidersNodeInformation(i, nodeIds[i], nodeDescriptions[i], nodeProviderToRegister); 69 nodeList.Add(nodeToAddFromProvider); 70 } 71 72 nodeProviders.Add(nodeProviderToRegister); 73 74 return true; 75 } 76 77 /// <summary> 78 /// Provide an array of INodeDescriptionsof the node provided by the node provider for the node. The index of the description 79 /// is the node index to which the description matches 80 /// </summary> 81 /// <returns></returns> GetNodeDescriptions()82 internal INodeDescription[] GetNodeDescriptions() 83 { 84 // The number of node descriptions is the number of nodes from all of the node providers, plus one for the default "0" node 85 INodeDescription[] nodeDescription = new INodeDescription[nodeList.Count+1]; 86 nodeDescription[0] = null; 87 for (int nodeListIndex = 0; nodeListIndex < nodeList.Count; nodeListIndex++) 88 { 89 ProvidersNodeInformation nodeInfo = nodeList[nodeListIndex]; 90 // +1 because the node description already has the 0 element set to null 91 nodeDescription[nodeListIndex + 1] = nodeInfo.Description; 92 } 93 return nodeDescription; 94 } 95 96 /// <summary> 97 /// Register node logger with all currently available providers 98 /// </summary> 99 /// <param name="loggerDescription"></param> RegisterNodeLogger(LoggerDescription loggerDescription)100 internal void RegisterNodeLogger(LoggerDescription loggerDescription) 101 { 102 foreach (INodeProvider nodeProvider in nodeProviders) 103 { 104 nodeProvider.RegisterNodeLogger(loggerDescription); 105 } 106 } 107 108 /// <summary> 109 /// Request status from all nodes in the system 110 /// </summary> 111 /// <param name="responseTimeout"></param> 112 /// <returns></returns> RequestStatusForNodes(int responseTimeout)113 internal NodeStatus[] RequestStatusForNodes(int responseTimeout) 114 { 115 int requestId = 0; 116 117 statusForNodes = new NodeStatus[nodeList.Count]; 118 statusReplyCount = 0; 119 statusMessageReceived.Reset(); 120 121 // Request status from all registered nodes 122 for (int i = 0; i < nodeList.Count; i++) 123 { 124 nodeList[i].NodeProvider.RequestNodeStatus(nodeList[i].NodeIndex, requestId); 125 } 126 127 long startTime = DateTime.Now.Ticks; 128 129 while (statusReplyCount < nodeList.Count) 130 { 131 if (statusMessageReceived.WaitOne(responseTimeout, false)) 132 { 133 // We received another reply 134 statusMessageReceived.Reset(); 135 // Calculate the time remaining and only continue if there is time left 136 TimeSpan timeSpent = new TimeSpan(DateTime.Now.Ticks - startTime); 137 startTime = DateTime.Now.Ticks; 138 responseTimeout = responseTimeout - (int)timeSpent.TotalMilliseconds; 139 if (responseTimeout <= 0) 140 { 141 Console.WriteLine("Response time out out exceeded :" + DateTime.Now.Ticks); 142 break; 143 } 144 } 145 else 146 { 147 // Timed out waiting for the response from the node 148 Console.WriteLine("Response time out out exceeded:" + DateTime.Now.Ticks); 149 break; 150 } 151 } 152 153 return statusForNodes; 154 } 155 156 PostNodeStatus(int nodeId, NodeStatus nodeStatus)157 internal void PostNodeStatus(int nodeId, NodeStatus nodeStatus) 158 { 159 ErrorUtilities.VerifyThrow( nodeStatus.RequestId != NodeStatus.UnrequestedStatus, 160 "Node manager should not receive unrequested status"); 161 162 NodeStatus[] currentStatus = statusForNodes; 163 164 for (int i = 0; i < nodeList.Count; i++) 165 { 166 if (nodeList[i].NodeId == nodeId) 167 { 168 currentStatus[i] = nodeStatus; 169 break; 170 } 171 } 172 173 statusReplyCount++; 174 statusMessageReceived.Set(); 175 } 176 177 PostCycleNotification( int nodeId, TargetInProgessState child, TargetInProgessState parent)178 internal void PostCycleNotification 179 ( 180 int nodeId, 181 TargetInProgessState child, 182 TargetInProgessState parent) 183 { 184 if (nodeId == 0) 185 { 186 parentEngine.Introspector.BreakCycle(child, parent); 187 } 188 189 for (int i = 0; i < nodeList.Count; i++) 190 { 191 if (nodeList[i].NodeId == nodeId) 192 { 193 nodeList[i].NodeProvider.PostIntrospectorCommand(nodeList[i].NodeIndex, child, parent); 194 break; 195 } 196 } 197 } 198 199 /// <summary> 200 /// Shut down each of the nodes for all providers registered to the node manager. 201 /// Shuts down the TEM. 202 /// </summary> ShutdownNodes(Node.NodeShutdownLevel nodeShutdownLevel)203 internal void ShutdownNodes(Node.NodeShutdownLevel nodeShutdownLevel) 204 { 205 foreach (INodeProvider nodeProvider in nodeProviders) 206 { 207 nodeProvider.ShutdownNodes(nodeShutdownLevel); 208 } 209 210 // Don't shutdown the TEM if the engine maybe reused for another build 211 if (nodeShutdownLevel != Node.NodeShutdownLevel.BuildCompleteFailure && 212 nodeShutdownLevel != Node.NodeShutdownLevel.BuildCompleteSuccess) 213 { 214 if (taskExecutionModule != null) 215 { 216 taskExecutionModule.Shutdown(); 217 taskExecutionModule = null; 218 // At this point we have nulled out the task execution module and have told our task worker threads to exit 219 // we do not want the engine build loop to continue to do any work becasue the operations of the build loop 220 // require the task execution module in many cases. Before this fix, when the engine build loop was allowed 221 // to do work after the task execution module we would get random null reference excetpions depending on 222 // what was the first line to use the TEM after it was nulled out. 223 parentEngine.SetEngineAbortTo(true); 224 } 225 } 226 } 227 228 /// <summary> 229 /// Post a build result to a node, the node index is an index into the list of nodes provided by all node providers 230 /// registered to the node manager, the 0 in index is a local call to taskexecutionmodule 231 /// </summary> 232 /// <param name="nodeIndex"></param> 233 /// <param name="buildResult"></param> PostBuildResultToNode(int nodeIndex, BuildResult buildResult)234 internal void PostBuildResultToNode(int nodeIndex, BuildResult buildResult) 235 { 236 ErrorUtilities.VerifyThrow(nodeIndex <= nodeList.Count, "Should not pass a node index higher then the number of nodes in nodeManager"); 237 if (nodeIndex != 0) 238 { 239 nodeList[nodeIndex-1].NodeProvider.PostBuildResultToNode(nodeList[nodeIndex-1].NodeIndex, buildResult); 240 } 241 else 242 { 243 taskExecutionModule.PostBuildResults(buildResult); 244 } 245 } 246 /// <summary> 247 /// Post a build request to a node, the node index is an index into the list of nodes provided by all node providers 248 /// registered to the node manager, the 0 in index is a local call to taskexecutionmodule 249 /// </summary> 250 /// <param name="nodeIndex"></param> 251 /// <param name="buildRequest"></param> PostBuildRequestToNode(int nodeIndex, BuildRequest buildRequest)252 internal void PostBuildRequestToNode(int nodeIndex, BuildRequest buildRequest) 253 { 254 ErrorUtilities.VerifyThrow(nodeIndex != 0, "Should not use NodeManager to post to local TEM"); 255 nodeList[nodeIndex - 1].NodeProvider.PostBuildRequestToNode(nodeList[nodeIndex - 1].NodeIndex, buildRequest); 256 } 257 258 /// <summary> 259 /// Execute a task on the local node 260 /// </summary> 261 /// <param name="taskState"></param> ExecuteTask(TaskExecutionState taskState)262 internal void ExecuteTask(TaskExecutionState taskState) 263 { 264 taskExecutionModule.ExecuteTask(taskState); 265 } 266 267 /// <summary> 268 /// TEMPORARY 269 /// </summary> UpdateSettings( bool enableOutofProcLogging, bool enableOnlyLogCriticalEvents, bool useBreadthFirstTraversal )270 internal void UpdateSettings 271 ( 272 bool enableOutofProcLogging, 273 bool enableOnlyLogCriticalEvents, 274 bool useBreadthFirstTraversal 275 ) 276 { 277 foreach (INodeProvider nodeProvider in nodeProviders) 278 { 279 nodeProvider.UpdateSettings(enableOutofProcLogging, enableOnlyLogCriticalEvents, useBreadthFirstTraversal); 280 } 281 } 282 ChangeNodeTraversalType(bool breadthFirstTraversal)283 internal void ChangeNodeTraversalType(bool breadthFirstTraversal) 284 { 285 UpdateSettings(parentEngine.EnabledCentralLogging, parentEngine.OnlyLogCriticalEvents, breadthFirstTraversal); 286 } 287 288 #endregion 289 290 #region Properties 291 292 /// <summary> 293 /// Getter access to the local node 294 /// </summary> 295 internal TaskExecutionModule TaskExecutionModule 296 { 297 get 298 { 299 return taskExecutionModule; 300 } 301 set 302 { 303 taskExecutionModule = value; 304 } 305 } 306 307 /// <summary> 308 /// Number of Nodes being managed by NodeManager 309 /// </summary> 310 internal int MaxNodeCount 311 { 312 get 313 { 314 // add 1 for the local node (taskExecutionModule) 315 return nodeList.Count+1; 316 } 317 } 318 #endregion 319 320 #region Data 321 /// <summary> 322 /// Pointer to the parent engine 323 /// </summary> 324 private Engine parentEngine; 325 /// <summary> 326 /// List of node information of nodes provided by registered node providers 327 /// </summary> 328 private List<ProvidersNodeInformation> nodeList; 329 /// <summary> 330 /// List of registered node providers 331 /// </summary> 332 private List<INodeProvider> nodeProviders; 333 /// <summary> 334 /// Array of status summaries from the node 335 /// </summary> 336 private NodeStatus[] statusForNodes; 337 /// <summary> 338 /// Count of status replies recieved 339 /// </summary> 340 private int statusReplyCount; 341 /// <summary> 342 /// An event activated when status message arrives 343 /// </summary> 344 private ManualResetEvent statusMessageReceived; 345 /// <summary> 346 /// Local TEM used for executing tasks within the current process 347 /// </summary> 348 private TaskExecutionModule taskExecutionModule; 349 #endregion 350 } 351 352 /// <summary> 353 /// Class which contains, information about each of the nodes provided by each of the node providers registered to node manager 354 /// </summary> 355 internal class ProvidersNodeInformation 356 { 357 #region Constructors ProvidersNodeInformation( int nodeProviderNodeIndex, int nodeId, INodeDescription nodeProviderDescription, INodeProvider nodeProviderReference )358 internal ProvidersNodeInformation 359 ( 360 int nodeProviderNodeIndex, 361 int nodeId, 362 INodeDescription nodeProviderDescription, 363 INodeProvider nodeProviderReference 364 ) 365 { 366 this.nodeIndex = nodeProviderNodeIndex; 367 this.nodeId = nodeId; 368 this.description = nodeProviderDescription; 369 this.nodeProvider = nodeProviderReference; 370 } 371 #endregion 372 373 #region Properties 374 /// <summary> 375 /// Node provider for node 376 /// </summary> 377 internal INodeProvider NodeProvider 378 { 379 get { return nodeProvider; } 380 } 381 382 /// <summary> 383 /// Node description for node 384 /// </summary> 385 internal INodeDescription Description 386 { 387 get 388 { 389 return description; 390 } 391 } 392 393 /// <summary> 394 /// Node index relative to the node provider to which it is attached 395 /// </summary> 396 internal int NodeIndex 397 { 398 get 399 { 400 return nodeIndex; 401 } 402 } 403 404 /// <summary> 405 /// The nodeId issued by the engine to this node 406 /// </summary> 407 internal int NodeId 408 { 409 get 410 { 411 return nodeId; 412 } 413 } 414 #endregion 415 416 #region Data 417 // Index from nodeProvider of a node which it manages 418 private int nodeIndex; 419 420 // Node description of node in nodeProvider referenced by nodeIndex; 421 private INodeDescription description; 422 423 // Reference to the nodeProvider which manages the node referenced by nodeIndex 424 private INodeProvider nodeProvider; 425 426 // The nodeId issued by the engine to this node 427 private int nodeId; 428 #endregion 429 } 430 } 431