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 using System.IO;
7 using System.Runtime.Serialization.Formatters.Binary;
8 
9 using Microsoft.Build.Framework;
10 using Microsoft.Build.BuildEngine.Shared;
11 
12 namespace Microsoft.Build.BuildEngine
13 {
14     /// <summary>
15     /// This class is a container for node status
16     /// </summary>
17     internal class NodeStatus
18     {
19         #region Constructors
20 
21         /// <summary>
22         /// Default constructor creating a NodeStatus
23         /// </summary>
NodeStatus( int requestId, bool isActive, int queueDepth, long lastTaskActivityTimeStamp, long lastEngineActivityTimeStamp, bool isLaunchInProgress )24         internal NodeStatus
25         (
26             int requestId,
27             bool isActive,
28             int queueDepth,
29             long lastTaskActivityTimeStamp,
30             long lastEngineActivityTimeStamp,
31             bool isLaunchInProgress
32         )
33         {
34             this.requestId = requestId;
35             this.isActive = isActive;
36             this.queueDepth = queueDepth;
37             this.lastTaskActivityTimeStamp = lastTaskActivityTimeStamp;
38             this.lastEngineActivityTimeStamp = lastEngineActivityTimeStamp;
39             this.isLaunchInProgress = isLaunchInProgress;
40             this.unhandledException = null;
41 
42             this.statusTimeStamp = DateTime.Now.Ticks;
43         }
44 
45         /// <summary>
46         /// Create a node status describing an unhandled error
47         /// </summary>
NodeStatus( Exception unhandledException )48         internal NodeStatus
49         (
50             Exception unhandledException
51         )
52         {
53             this.requestId = UnrequestedStatus;
54             this.isActive = true;
55             this.isLaunchInProgress = false;
56             this.unhandledException = unhandledException;
57 
58             this.statusTimeStamp = DateTime.Now.Ticks;
59         }
60 
61         /// <summary>
62         /// Create a node status indicating that breadth first traversal should be used
63         /// </summary>
NodeStatus( bool useBreadthFirstTraversal )64         internal NodeStatus
65         (
66             bool useBreadthFirstTraversal
67         )
68         {
69             this.requestId = UnrequestedStatus;
70             this.isActive = true;
71             this.isLaunchInProgress = false;
72             this.unhandledException = null;
73             this.traversalType = useBreadthFirstTraversal;
74         }
75 
76         /// <summary>
77         /// Create a node status indicating that node process has exited
78         /// </summary>
NodeStatus( int requestId )79         internal NodeStatus
80         (
81             int requestId
82         )
83         {
84             this.requestId = requestId;
85             this.isActive = true;
86             this.isLaunchInProgress = false;
87             this.unhandledException = null;
88             this.hasExited = true;
89         }
90         #endregion
91 
92         #region Properties
93 
94         /// <summary>
95         /// The time period for which the node has been idle when the status report was filled out
96         /// </summary>
97         internal long TimeSinceLastTaskActivity
98         {
99             get
100             {
101                 return (statusTimeStamp - lastTaskActivityTimeStamp);
102             }
103         }
104 
105 
106         /// <summary>
107         /// The time period for which the node has been idle when the status report was filled out
108         /// </summary>
109         internal long TimeSinceLastLoopActivity
110         {
111             get
112             {
113                 return (statusTimeStamp - lastEngineActivityTimeStamp);
114             }
115         }
116 
117         /// <summary>
118         /// The time stamp at which the node was last active
119         /// </summary>
120         internal long LastTaskActivity
121         {
122             get
123             {
124                 return lastTaskActivityTimeStamp;
125             }
126         }
127 
128         /// <summary>
129         /// The time stamp at which there was activity in the node's build loop
130         /// </summary>
131         internal long LastLoopActivity
132         {
133             get
134             {
135                 return lastEngineActivityTimeStamp;
136             }
137         }
138 
139         /// <summary>
140         /// True if the node is active (i.e. has been launched and can accept commands)
141         /// </summary>
142         internal bool IsActive
143         {
144             get
145             {
146                 return this.isActive;
147             }
148         }
149 
150         /// <summary>
151         /// True if the node process is no longer alive
152         /// </summary>
153         internal bool HasExited
154         {
155             get
156             {
157                 return this.hasExited;
158             }
159         }
160 
161         /// <summary>
162         /// The token of the request to which this is a response (-1 if status is unrequested)
163         /// </summary>
164         internal int RequestId
165         {
166             get
167             {
168                 return this.requestId;
169             }
170         }
171 
172         /// <summary>
173         /// The number of requests that need to be processed
174         /// </summary>
175         internal int QueueDepth
176         {
177             get
178             {
179                 return this.queueDepth;
180             }
181         }
182 
183         /// <summary>
184         /// The state of the targets which are in progress on the node
185         /// </summary>
186         internal TargetInProgessState [] StateOfInProgressTargets
187         {
188             get
189             {
190                 return this.stateOfInProgressTargets;
191             }
192             set
193             {
194                 this.stateOfInProgressTargets = value;
195             }
196         }
197 
198         /// <summary>
199         /// True if the node is in the process of being launched, but is not yet active
200         /// </summary>
201         internal bool IsLaunchInProgress
202         {
203             get
204             {
205                 return isLaunchInProgress;
206             }
207         }
208 
209         /// <summary>
210         /// Returns the exception that occured on the node
211         /// </summary>
212         internal Exception UnhandledException
213         {
214             get
215             {
216                 return unhandledException;
217             }
218         }
219 
220         internal bool TraversalType
221         {
222             get
223             {
224                 return traversalType;
225             }
226         }
227         #endregion
228 
229         #region Data
230         private long statusTimeStamp; // the timestamp indicating when this status structure was filled out
231         private int  requestId; // the token of the request to which this is a response (-1 if status is unrequested)
232         private bool isActive; // is the node active
233         private bool isLaunchInProgress; // is the node in the process of being launched
234         private int  queueDepth; // the number of build request in the node's queue
235         private long lastTaskActivityTimeStamp; // the time stamp of the last task activity
236         private long lastEngineActivityTimeStamp; // the time stamp of the last engine activity
237         private TargetInProgessState[] stateOfInProgressTargets;
238         private Exception unhandledException; // unhandled exception
239         private bool traversalType; // if true use breadth first traversal
240         private bool hasExited; // if true the node process is no longer  alive
241         private static BinaryFormatter formatter = new BinaryFormatter();
242         internal const int UnrequestedStatus = -1; // used to indicate that the node is generating status without request
243         #endregion
244 
245         #region CustomSerializationToStream
WriteToStream(BinaryWriter writer)246         internal void WriteToStream(BinaryWriter writer)
247         {
248             writer.Write(traversalType);
249             writer.Write((Int64)statusTimeStamp);
250             writer.Write((Int32)requestId);
251             writer.Write(isActive);
252             writer.Write(isLaunchInProgress);
253             writer.Write((Int32)queueDepth);
254             writer.Write((Int64)lastTaskActivityTimeStamp);
255             writer.Write((Int64)lastEngineActivityTimeStamp);
256 
257             if (stateOfInProgressTargets == null)
258             {
259                 writer.Write((byte)0);
260             }
261             else
262             {
263                 writer.Write((byte)1);
264                 writer.Write((Int32)stateOfInProgressTargets.Length);
265                 for (int i = 0; i < stateOfInProgressTargets.Length; i++)
266                 {
267                     if (stateOfInProgressTargets[i] == null)
268                     {
269                         writer.Write((byte)0);
270                     }
271                     else
272                     {
273                        writer.Write((byte)1);
274                        stateOfInProgressTargets[i].WriteToStream(writer);
275                     }
276                 }
277             }
278 
279             if (unhandledException == null)
280             {
281                 writer.Write((byte)0);
282             }
283             else
284             {
285                 writer.Write((byte)1);
286                 formatter.Serialize(writer.BaseStream, unhandledException);
287             }
288         }
289 
CreateFromStream(BinaryReader reader)290         internal static NodeStatus CreateFromStream(BinaryReader reader)
291         {
292             NodeStatus status = new NodeStatus(null);
293             status.traversalType = reader.ReadBoolean();
294             status.statusTimeStamp = reader.ReadInt64();
295             status.requestId = reader.ReadInt32();
296             status.isActive = reader.ReadBoolean();
297             status.isLaunchInProgress = reader.ReadBoolean();
298             status.queueDepth = reader.ReadInt32();
299             status.lastTaskActivityTimeStamp = reader.ReadInt64();
300             status.lastEngineActivityTimeStamp = reader.ReadInt64();
301 
302             if (reader.ReadByte() == 0)
303             {
304                 status.stateOfInProgressTargets = null;
305             }
306             else
307             {
308                 int numberOfInProgressTargets = reader.ReadInt32();
309                 status.stateOfInProgressTargets = new TargetInProgessState[numberOfInProgressTargets];
310                 for (int i = 0; i < numberOfInProgressTargets; i++)
311                 {
312                     if (reader.ReadByte() == 0)
313                     {
314                         status.stateOfInProgressTargets[i] = null;
315                     }
316                     else
317                     {
318                         TargetInProgessState state = new TargetInProgessState();
319                         state.CreateFromStream(reader);
320                         status.stateOfInProgressTargets[i] = state;
321                     }
322                 }
323             }
324 
325             if (reader.ReadByte() == 0)
326             {
327                 status.unhandledException = null;
328             }
329             else
330             {
331                 status.unhandledException = (Exception)formatter.Deserialize(reader.BaseStream);
332             }
333             return status;
334         }
335         #endregion
336     }
337 }
338