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