4 using System.Collections.Generic;
5 using System.Diagnostics;
7 using System.Threading;
9 using SiliconStudio.Core.Diagnostics;
10 using SiliconStudio.Core.MicroThreading;
11 using System.ServiceModel;
13 namespace SiliconStudio.BuildEngine
15 internal class BuildThreadMonitor
20 private class BuildStepInfo
22 public readonly BuildStep BuildStep;
23 public readonly
long ExecutionId;
24 public readonly
string Description;
26 public bool HasBeenSend {
get;
private set; }
28 public BuildStepInfo(BuildStep buildStep,
long executionId,
string description,
TimestampLocalLogger logger)
30 BuildStep = buildStep;
31 ExecutionId = executionId;
32 Description = description;
37 public void BuildStepSent()
42 public override string ToString()
44 return "[" + ExecutionId +
"] " + BuildStep;
48 private readonly Dictionary<int, List<TimeInterval>> threadExecutionIntervals =
new Dictionary<int, List<TimeInterval>>();
50 private readonly List<BuildStepInfo> buildStepInfos =
new List<BuildStepInfo>();
51 private readonly List<BuildStepInfo> buildStepInfosToSend =
new List<BuildStepInfo>();
52 private readonly List<long> buildStepResultsToSend =
new List<long>();
54 private readonly List<MicrothreadNotification> microthreadNotifications =
new List<MicrothreadNotification>();
56 private readonly Guid builderId;
57 private readonly
string monitorPipeName;
59 private readonly Stopwatch stopWatch =
new Stopwatch();
61 private DateTime startTime;
64 private static readonly
double TickFactor = 10000000.0 / Stopwatch.Frequency;
66 private IBuildMonitorRemote buildMonitorRemote;
70 private Thread monitorThread;
72 internal BuildThreadMonitor(
Scheduler scheduler, Guid builderId,
string monitorPipeName = Builder.MonitorPipeName)
74 this.monitorPipeName = monitorPipeName;
75 this.builderId = builderId;
77 scheduler.MicroThreadStarted += MicroThreadStarted;
78 scheduler.MicroThreadEnded += MicroThreadEnded;
79 scheduler.MicroThreadCallbackStart += MicroThreadCallbackStart;
80 scheduler.MicroThreadCallbackEnd += MicroThreadCallbackEnd;
84 internal void RegisterThread(
int threadId)
86 lock (threadExecutionIntervals)
88 threadExecutionIntervals.Add(threadId,
new List<TimeInterval>());
94 lock (buildStepInfosToSend)
96 buildStepInfosToSend.Add(
new BuildStepInfo(buildStep, buildStep.ExecutionId, buildStep.Description, logger));
102 startTime = DateTime.Now;
107 if (TryConnectMonitor())
108 buildMonitorRemote.StartBuild(builderId, startTime);
114 delay = SendThreadUpdate() ? 300 : 1000;
118 if (TryConnectMonitor())
119 buildMonitorRemote.EndBuild(builderId, DateTime.Now);
124 var communicationObj = buildMonitorRemote as ICommunicationObject;
126 if (communicationObj != null)
127 communicationObj.Close();
136 { IsBackground =
true, Name =
"Monitor Thread" };
138 monitorThread.Start();
148 if (monitorThread != null)
150 monitorThread.Join();
151 monitorThread = null;
155 private bool TryConnectMonitor()
157 if (buildMonitorRemote == null)
161 var namedPipeBinding =
new NetNamedPipeBinding(NetNamedPipeSecurityMode.None) { SendTimeout = TimeSpan.FromSeconds(300.0) };
162 buildMonitorRemote = ChannelFactory<IBuildMonitorRemote>.CreateChannel(namedPipeBinding,
new EndpointAddress(monitorPipeName));
163 buildMonitorRemote.Ping();
165 catch (EndpointNotFoundException)
167 buildMonitorRemote = null;
171 return buildMonitorRemote != null;
174 private bool SendThreadUpdate()
176 if (!TryConnectMonitor())
180 var localMicroThreadNotifications =
new List<MicrothreadNotification>();
181 var localBuildStepResultsToSend =
new List<long>();
183 lock (microthreadNotifications)
185 localMicroThreadNotifications.AddRange(microthreadNotifications);
186 microthreadNotifications.Clear();
189 lock (buildStepInfosToSend)
191 buildStepInfos.AddRange(buildStepInfosToSend);
192 buildStepInfosToSend.Clear();
195 lock (buildStepResultsToSend)
197 localBuildStepResultsToSend.AddRange(buildStepResultsToSend);
198 buildStepResultsToSend.Clear();
204 foreach (var buildStepInfo
in buildStepInfos.Where(x => !x.HasBeenSend))
206 buildMonitorRemote.SendBuildStepInfo(builderId, buildStepInfo.ExecutionId, buildStepInfo.Description, startTime);
207 buildStepInfo.BuildStepSent();
210 buildMonitorRemote.SendMicrothreadEvents(builderId, startTime, DateTime.Now, localMicroThreadNotifications);
213 foreach (var buildStepInfo
in buildStepInfos)
215 if (buildStepInfo.Logger != null)
218 lock (buildStepInfo.Logger)
220 if (buildStepInfo.Logger.Messages.Count > 0)
222 messages = buildStepInfo.Logger.Messages.ToArray();
223 buildStepInfo.Logger.Messages.Clear();
226 if (messages != null)
230 var serializableMessages = (messages.Select(x =>
new SerializableTimestampLogMessage(x))).ToList();
231 buildMonitorRemote.SendCommandLog(builderId, startTime, buildStepInfo.ExecutionId, serializableMessages);
235 lock (buildStepInfo.Logger)
237 buildStepInfo.Logger.Messages.InsertRange(0, messages);
246 for (
int i = localBuildStepResultsToSend.Count - 1; i >= 0; --i)
248 long microthreadId = localBuildStepResultsToSend[i];
249 BuildStepInfo stepInfo = buildStepInfos.SingleOrDefault(x => x.ExecutionId == microthreadId);
250 if (stepInfo != null && stepInfo.BuildStep != null)
252 buildMonitorRemote.SendBuildStepResult(builderId, startTime, microthreadId, stepInfo.BuildStep.Status);
253 localBuildStepResultsToSend.RemoveAt(i);
259 lock (microthreadNotifications)
261 microthreadNotifications.AddRange(localMicroThreadNotifications);
264 buildMonitorRemote = null;
268 if (localBuildStepResultsToSend.Count > 0)
270 lock (buildStepResultsToSend)
272 buildStepResultsToSend.AddRange(localBuildStepResultsToSend);
276 return buildMonitorRemote != null;
286 lock (buildStepResultsToSend)
288 buildStepResultsToSend.Add(e.MicroThread.Id);
294 TimeInterval timeInterval;
296 lock (threadExecutionIntervals)
298 List<TimeInterval> intervals = threadExecutionIntervals[e.ThreadId];
299 if (intervals.Count > 0 && !intervals.Last().HasEnded)
300 throw new InvalidOperationException(
"Starting a new microthread on a thread still running another microthread.");
302 timeInterval =
new TimeInterval(GetTicksFromStopwatch());
303 intervals.Add(timeInterval);
304 intervalCount = intervals.Count;
308 long jobId = GetMicrothreadJobIdFromThreadInfo(e.
ThreadId, intervalCount);
309 var jobInfo =
new MicrothreadNotification(e.
ThreadId, e.
MicroThread.
Id, jobId, timeInterval.StartTime, MicrothreadNotification.NotificationType.JobStarted);
311 lock (microthreadNotifications)
313 microthreadNotifications.Add(jobInfo);
319 long endTime = GetTicksFromStopwatch();
321 lock (threadExecutionIntervals)
323 List<TimeInterval> intervals = threadExecutionIntervals[e.ThreadId];
324 intervals.Last().
End(endTime);
325 intervalCount = intervals.Count;
327 long jobId = GetMicrothreadJobIdFromThreadInfo(e.
ThreadId, intervalCount);
328 var jobInfo =
new MicrothreadNotification(e.
ThreadId, e.
MicroThread.
Id, jobId, endTime, MicrothreadNotification.NotificationType.JobEnded);
330 lock (microthreadNotifications)
332 microthreadNotifications.Add(jobInfo);
336 private long GetMicrothreadJobIdFromThreadInfo(
int threadId,
int threadIntervalCount)
340 long result = threadIntervalCount;
341 result += ((long)threadId) << (
sizeof(int) * 8);
346 private long GetTicksFromStopwatch()
348 return (
long)(stopWatch.ElapsedTicks * TickFactor);
A structure describing a log message associated with a timestamp.
static ThreadStart Wrap(ThreadStart action, [CallerFilePath] string sourceFilePath="", [CallerMemberName] string memberName="", [CallerLineNumber] int sourceLineNumber=0)
Base implementation for ILogger.
MicroThread MicroThread
Gets or sets the MicroThread this event concerns.
long Id
Gets the id of this MicroThread.
Scheduler that manage a group of cooperating MicroThread.
A logger that stores messages locally with their timestamp, useful for internal log scenarios...
int ThreadId
Gets or sets the System.Threading.Thread.ManagedThreadId active when this event happened.
Provides data for the Scheduler.MicroThreadStarted, Scheduler.MicroThreadEnded, Scheduler.MicroThreadCallbackStart and Scheduler.MicroThreadCallbackEnd events.