Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
MicroThreadMonitoringManager.cs
Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using SiliconStudio.Paradox.Framework;
6 using SiliconStudio.Paradox.Framework.MicroThreading;
7 using SiliconStudio.Core.Extensions;
8 using System.Diagnostics;
9 using System.Threading;
10 using SiliconStudio.Paradox.Framework.Time;
11 using SiliconStudio.Paradox.DebugTools.DataStructures;
12 using SiliconStudio.Presentation.Observable;
13 
14 namespace SiliconStudio.Paradox.DebugTools
15 {
16  /// <summary>
17  /// Manager class that monitors the micro threads executions.
18  /// <remarks>This class registers to <c>Scheduler</c> events and listen to <c>MicroThread</c>s state changes.
19  /// It provides monitoring information through <c>IObservable</c> notifications.</remarks>
20  /// </summary>
21  public class MicroThreadMonitoringManager : IObservable<FrameInfo>
22  {
23  /// <summary>
24  /// Maximum number of micro threads execution frames stored by the monitoring manager.
25  /// </summary>
26  public const int MaximumCapturedFrames = 30;
27 
28  public Scheduler Scheduler { get; private set; }
29  public uint CurrentFrameNumber { get { return frameNumber; } }
30 
31  private readonly AbsoluteStopwatch stopwatch = new AbsoluteStopwatch();
32  private readonly ObserverContainer<FrameInfo> observerContainer = new ObserverContainer<FrameInfo>();
33 
34  private readonly object processInfoLock = new object();
35  private ProcessInfo processInfo = new ProcessInfo();
36  private FrameInfo frameInfo;
37  private uint frameNumber;
38 
39  private readonly Dictionary<long, MicroThreadPendingState> pendingMicroThreads = new Dictionary<long, MicroThreadPendingState>();
40 
41  public MicroThreadMonitoringManager(Scheduler scheduler)
42  {
43  if (scheduler == null)
44  throw new ArgumentNullException("scheduler");
45 
46  Scheduler = scheduler;
47  }
48 
49  /// <summary>
50  /// Registers an observer for further <c>FrameInfo</c> notifications.
51  /// </summary>
52  /// <param name="observer">Observer that will receive outgoing <c>FrameInfo</c> notifications.</param>
53  /// <returns>Returns the subscription token.</returns>
54  public IDisposable Subscribe(IObserver<FrameInfo> observer)
55  {
56  return observerContainer.Subscribe(observer);
57  }
58 
59  /// <summary>
60  /// Starts the micro threads monitoring.
61  /// <remarks>It resets the time and the frame number.</remarks>
62  /// </summary>
63  public void StartMonitoring()
64  {
65  frameNumber = 0;
66  // New MicroThread system doesn't have any PropertyChanged event yet.
67  throw new NotImplementedException();
68  //Scheduler.MicroThreadStateChanged += OnMicroThreadStateChanged;
69  //Scheduler.MessageLoopBegin += OnMessageLoopBegin;
70  stopwatch.Start();
71  }
72 
73  /// <summary>
74  /// Stops the micro threads monitoring.
75  /// </summary>
76  public void StopMonitoring()
77  {
78  // New MicroThread system doesn't have any PropertyChanged event yet.
79  throw new NotImplementedException();
80  //Scheduler.MessageLoopBegin -= OnMessageLoopBegin;
81  //Scheduler.MicroThreadStateChanged -= OnMicroThreadStateChanged;
82  }
83 
84  /// <summary>
85  /// Deletes all the previously monitored frame data.
86  /// <remarks>It also resets time and the frame number.</remarks>
87  /// </summary>
88  public void ClearMonitoringData()
89  {
90  frameNumber = 0;
91  lock (processInfoLock)
92  {
93  processInfo = new ProcessInfo();
94  }
95  GC.Collect();
96  stopwatch.Start();
97  }
98 
99  /// <summary>
100  /// Retrieves the already monitored and stored data.
101  /// <remarks>Beware of using these data due to non thread-safety!</remarks>
102  /// </summary>
103  /// <returns></returns>
105  {
106  return processInfo;
107  }
108 
109  /// <summary>
110  /// Take a snapshot of the monitored data.
111  /// <remarks>This set of data is thread-safely acquired.</remarks>
112  /// </summary>
113  /// <returns></returns>
115  {
116  ProcessInfo duplicate;
117 
118  lock (processInfoLock)
119  {
120  duplicate = processInfo.Duplicate();
121  }
122 
123  return duplicate;
124  }
125 
126  private void OnMicroThreadStateChanged(object sender, SchedulerEventArgs e)
127  {
128  if (e.MicroThreadPreviousState == MicroThreadState.None)
129  return;
130 
131  if (frameInfo == null)
132  return;
133 
134  double currentTime = stopwatch.Elapsed;
135 
136  int threadId = Thread.CurrentThread.ManagedThreadId;
137  long microThreadId = e.MicroThread.Id;
138 
139  ThreadInfo threadInfo = frameInfo.ThreadItems.FirstOrDefault(ti => ti.Id == threadId);
140  if (threadInfo == null)
141  {
142  threadInfo = new ThreadInfo { Id = threadId };
143  frameInfo.ThreadItems.Add(threadInfo);
144  }
145 
146  // pending state is used to keep trace of the micro threads recently added as 'running'
147  // in order to create a proper MicroThreadInfo item when then receiving a 'waiting' notification
148  MicroThreadPendingState pendingState;
149  if (pendingMicroThreads.TryGetValue(microThreadId, out pendingState))
150  {
151  threadInfo.MicroThreadItems.Add(new MicroThreadInfo
152  {
153  Id = microThreadId,
154  BeginState = pendingState.State,
155  EndState = e.MicroThread.State,
156  BeginTime = Math.Max(pendingState.Time, frameInfo.BeginTime),
157  EndTime = currentTime,
158  });
159 
160  pendingMicroThreads.Remove(microThreadId);
161  }
162  else if (e.MicroThread.IsOver == false)
163  {
164  pendingMicroThreads.Add(microThreadId, new MicroThreadPendingState
165  {
166  ThreadId = threadInfo.Id,
167  Time = currentTime,
168  State = e.MicroThread.State,
169  MicroThread = e.MicroThread,
170  });
171  }
172  }
173 
174  private void OnMessageLoopBegin(object sender, EventArgs e)
175  {
176  double currentTime = stopwatch.Elapsed;
177 
178  if (frameInfo != null)
179  {
180  frameInfo.EndTime = currentTime;
181 
182  foreach (MicroThreadPendingState pendingState in pendingMicroThreads.Values)
183  {
184  ThreadInfo th = frameInfo.ThreadItems.FirstOrDefault(ti => ti.Id == pendingState.ThreadId);
185  if (th == null)
186  throw new InvalidOperationException("Thread " + pendingState.ThreadId + " is not referenced in frame " + frameInfo.FrameNumber);
187 
188  th.MicroThreadItems.Add(new MicroThreadInfo
189  {
190  Id = pendingState.MicroThread.Id,
191  BeginState = pendingState.State,
192  EndState = pendingState.State,
193  BeginTime = Math.Max(pendingState.Time, frameInfo.BeginTime),
194  EndTime = currentTime,
195  });
196  }
197 
198  // end of the previous frame
199  lock (observerContainer.SyncRoot)
200  {
201  observerContainer.Observers.ForEach(observer => observer.OnNext(frameInfo));
202  }
203 
204  // begining of the new frame
205  lock (processInfoLock)
206  {
207  processInfo.Frames.Add(frameInfo);
208  }
209  }
210 
211  frameInfo = new FrameInfo
212  {
213  BeginTime = currentTime,
214  FrameNumber = frameNumber++,
215  };
216 
217  lock (processInfoLock)
218  {
219  if (processInfo.Frames.Count > MaximumCapturedFrames)
220  processInfo.Frames.RemoveAt(0);
221  }
222  }
223  }
224 }
Manager class that monitors the micro threads executions.
void ClearMonitoringData()
Deletes all the previously monitored frame data.
ProcessInfo GetProcessInfoData()
Retrieves the already monitored and stored data.
ProcessInfo TakeProcessInfoDataSnapshot()
Take a snapshot of the monitored data.
IDisposable Subscribe(IObserver< FrameInfo > observer)
Registers an observer for further FrameInfo notifications.