Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
MicroThread.cs
Go to the documentation of this file.
1 // Copyright (c) 2014 Silicon Studio Corp. (http://siliconstudio.co.jp)
2 // This file is distributed under GPL v3. See LICENSE.md for details.
3 using System;
4 using System.Collections.Generic;
5 using System.Diagnostics;
6 using System.Threading;
7 using System.Threading.Tasks;
8 using SiliconStudio.Core.Collections;
9 using SiliconStudio.Core.Diagnostics;
10 
11 namespace SiliconStudio.Core.MicroThreading
12 {
13  /// <summary>
14  /// Represents an execution context managed by a <see cref="Scheduler"/>, that can cooperatively yield execution to another <see cref="MicroThread"/> at any point (usually using async calls).
15  /// </summary>
16  public class MicroThread : IComparable<MicroThread>
17  {
18  internal ProfilingKey ProfilingKey;
19 
20  /// <summary>
21  /// Gets the attached properties to this component.
22  /// </summary>
24 
25  private static long globalCounterId;
26 
27  private class CallbackFromActionBuilder
28  {
29  public Action MicroThreadAction { private get; set; }
30 
31  public Action MicroThreadCallback { get; private set; }
32 
33  public CallbackFromActionBuilder(MicroThread microThread)
34  {
35  MicroThreadCallback = () =>
36  {
37  microThread.ThrowIfExceptionRequest();
38  MicroThreadAction();
39  microThread.ThrowIfExceptionRequest();
40  };
41  }
42  }
43  private class CallbackSendPostCallbackBuilder
44  {
45  public SendOrPostCallback SendOrPostCallback { private get; set; }
46  public object CallbackState { private get; set; }
47 
48  public Action MicroThreadCallback { get; private set; }
49 
50  public CallbackSendPostCallbackBuilder(MicroThread microThread)
51  {
52  MicroThreadCallback = () =>
53  {
54  microThread.ThrowIfExceptionRequest();
55  SendOrPostCallback(CallbackState);
56  microThread.ThrowIfExceptionRequest();
57  };
58  }
59  }
60 
61  // Counter that will be used to have a "stable" microthread scheduling (first added is first scheduled)
62  private int priority;
63  private long schedulerCounter;
64 
65  private int state;
66  internal PriorityQueueNode<MicroThread> ScheduledLinkedListNode;
67  internal LinkedListNode<MicroThread> AllLinkedListNode; // Also used as lock for "CompletionTask"
68  internal Action Callback;
70 
71  private readonly CallbackFromActionBuilder callbackFromActionBuilder;
72  private readonly CallbackSendPostCallbackBuilder callbackSendPostCallbackBuilder;
73 
75  {
76  Id = Interlocked.Increment(ref globalCounterId);
77  Scheduler = scheduler;
78  ScheduledLinkedListNode = new PriorityQueueNode<MicroThread>(this);
79  AllLinkedListNode = new LinkedListNode<MicroThread>(this);
80  ScheduleMode = ScheduleMode.Last;
81  Flags = flags;
82  Tags = new PropertyContainer(this);
83 
84  callbackFromActionBuilder = new CallbackFromActionBuilder(this);
85  callbackSendPostCallbackBuilder = new CallbackSendPostCallbackBuilder(this);
86  }
87 
88  /// <summary>
89  /// Gets or sets the priority of this <see cref="MicroThread"/>.
90  /// </summary>
91  /// <value>
92  /// The priority.
93  /// </value>
94  public int Priority
95  {
96  get { return priority; }
97  set { priority = value; }
98  }
99 
100  /// <summary>
101  /// Gets the id of this <see cref="MicroThread"/>.
102  /// </summary>
103  /// <value>
104  /// The id.
105  /// </value>
106  public long Id { get; private set; }
107 
108  /// <summary>
109  /// Gets or sets the name of this <see cref="MicroThread"/>.
110  /// </summary>
111  /// <value>
112  /// The name.
113  /// </value>
114  public string Name { get; set; }
115 
116  /// <summary>
117  /// Gets the scheduler associated with this <see cref="MicroThread"/>.
118  /// </summary>
119  /// <value>The scheduler associated with this <see cref="MicroThread"/>.</value>
120  public Scheduler Scheduler { get; private set; }
121 
122  /// <summary>
123  /// Gets the state of this <see cref="MicroThread"/>.
124  /// </summary>
125  /// <value>The state of this <see cref="MicroThread"/>.</value>
126  public MicroThreadState State { get { return (MicroThreadState)state; } internal set { state = (int)value; } }
127 
128  /// <summary>
129  /// Gets the exception that was thrown by this <see cref="MicroThread"/>.
130  /// </summary>
131  /// It could come from either internally, or from <see cref="RaiseException"/> if it was successfully processed.
132  /// <value>The exception.</value>
133  public Exception Exception { get; private set; }
134 
135  /// <summary>
136  /// Gets the <see cref="MicroThread"/> flags.
137  /// </summary>
138  /// <value>
139  /// The flags.
140  /// </value>
141  public MicroThreadFlags Flags { get; private set; }
142 
143  /// <summary>
144  /// Gets or sets the <see cref="MicroThread"/> scheduling mode.
145  /// </summary>
146  /// <value>
147  /// The scheduling mode.
148  /// </value>
149  public ScheduleMode ScheduleMode { get; set; }
150 
151  /// <summary>
152  /// Gets or sets the exception to raise.
153  /// </summary>
154  /// <value>The exception to raise.</value>
155  internal Exception ExceptionToRaise { get; set; }
156 
157  /// <summary>
158  /// Gets or sets the task that will be executed upon completion (used internally for <see cref="Scheduler.WhenAll"/>)
159  /// </summary>
160  internal TaskCompletionSource<int> CompletionTask { get; set; }
161 
162  /// <summary>
163  /// Indicates whether the MicroThread is terminated or not, either in Completed, Cancelled or Failed status.
164  /// </summary>
165  public bool IsOver
166  {
167  get
168  {
169  return
170  State == MicroThreadState.Completed ||
171  State == MicroThreadState.Cancelled ||
172  State == MicroThreadState.Failed;
173  }
174  }
175 
176  /// <summary>
177  /// Gets the current micro thread (self).
178  /// </summary>
179  /// <value>The current micro thread (self).</value>
180  public static MicroThread Current
181  {
182  get { return Scheduler.CurrentMicroThread; }
183  }
184 
185  public int CompareTo(MicroThread other)
186  {
187  var priorityDiff = priority.CompareTo(other.priority);
188  if (priorityDiff != 0)
189  return priorityDiff;
190 
191  return schedulerCounter.CompareTo(other.schedulerCounter);
192  }
193 
194  public void Migrate(Scheduler scheduler)
195  {
196  throw new NotImplementedException();
197  }
198 
199  public void Remove()
200  {
201  throw new NotImplementedException();
202  }
203 
204  /// <summary>
205  /// Starts this <see cref="MicroThread"/> with the specified function.
206  /// </summary>
207  /// <param name="microThreadFunction">The micro thread function.</param>
208  /// <param name="flags">The flags.</param>
209  /// <param name="scheduleMode">The schedule mode.</param>
210  /// <exception cref="System.InvalidOperationException">MicroThread was already started before.</exception>
211  public void Start(Func<Task> microThreadFunction, ScheduleMode scheduleMode = ScheduleMode.Last)
212  {
213  ProfilingKey = new ProfilingKey("MicroThread " + microThreadFunction.Target);
214 
215  // TODO: Interlocked compare exchange?
216  if (Interlocked.CompareExchange(ref state, (int)MicroThreadState.Starting, (int)MicroThreadState.None) != (int)MicroThreadState.None)
217  throw new InvalidOperationException("MicroThread was already started before.");
218 
219  Action wrappedMicroThreadFunction = async () =>
220  {
221  try
222  {
223  State = MicroThreadState.Running;
224 
225  await microThreadFunction();
226 
227  if (State != MicroThreadState.Running)
228  throw new InvalidOperationException("MicroThread completed in an invalid state.");
229  State = MicroThreadState.Completed;
230  }
231  catch (Exception e)
232  {
233  Scheduler.Log.Error("Unexpected exception while executing a micro-thread. Reason: {0}", new object[] {e});
234  SetException(e);
235  }
236  finally
237  {
238  lock (Scheduler.allMicroThreads)
239  {
240  Scheduler.allMicroThreads.Remove(AllLinkedListNode);
241  }
242  }
243  };
244 
245  Action callback = () =>
246  {
248  SynchronizationContext.SetSynchronizationContext(SynchronizationContext);
249 
250  wrappedMicroThreadFunction();
251  };
252 
253  lock (Scheduler.allMicroThreads)
254  {
255  Scheduler.allMicroThreads.AddLast(AllLinkedListNode);
256  }
257 
258  ScheduleContinuation(scheduleMode, callback);
259  }
260 
261  /// <summary>
262  /// Yields to this <see cref="MicroThread"/>.
263  /// </summary>
264  /// <returns>Task.</returns>
265  public async Task Run()
266  {
267  Reschedule(ScheduleMode.First);
268  var currentScheduler = Scheduler.Current;
269  if (currentScheduler == Scheduler)
270  await Scheduler.Yield();
271  }
272 
273  /// <summary>
274  /// Raises an exception from within the <see cref="MicroThread"/>.
275  /// </summary>
276  /// As an exception can only be raised cooperatively, there is no guarantee it will actually happen or when it will happen.
277  /// The scheduler usually checks for them before and after a continuation is running.
278  /// Only one Exception is currently recorded.
279  /// <param name="e">The exception.</param>
280  public void RaiseException(Exception e)
281  {
282  if (ExceptionToRaise == null)
283  ExceptionToRaise = e;
284  }
285 
286  internal void SetException(Exception exception)
287  {
288  Exception = exception;
289 
290  // Depending on if exception was raised from outside or inside, set appropriate state
291  State = (exception == ExceptionToRaise) ? MicroThreadState.Cancelled : MicroThreadState.Failed;
292  }
293 
294  internal void Reschedule(ScheduleMode scheduleMode)
295  {
296  lock (Scheduler.scheduledMicroThreads)
297  {
298  if (ScheduledLinkedListNode.Index != -1)
299  {
300  Scheduler.scheduledMicroThreads.Remove(ScheduledLinkedListNode);
301 
302  Schedule(scheduleMode);
303  }
304  }
305  }
306 
307  internal void ScheduleContinuation(ScheduleMode scheduleMode, SendOrPostCallback callback, object callbackState)
308  {
309  Debug.Assert(callback != null);
310  lock (Scheduler.scheduledMicroThreads)
311  {
312  callbackSendPostCallbackBuilder.SendOrPostCallback = callback;
313  callbackSendPostCallbackBuilder.CallbackState = callbackState;
314  Callback += callbackSendPostCallbackBuilder.MicroThreadCallback;
315 
316  if (ScheduledLinkedListNode.Index != -1)
317  throw new InvalidOperationException("MicroThread was already scheduled, something is probably wrong.");
318 
319  Schedule(scheduleMode);
320  }
321  }
322 
323  internal void ScheduleContinuation(ScheduleMode scheduleMode, Action callback)
324  {
325  Debug.Assert(callback != null);
326  lock (Scheduler.scheduledMicroThreads)
327  {
328  callbackFromActionBuilder.MicroThreadAction = callback;
329  Callback += callbackFromActionBuilder.MicroThreadCallback;
330 
331  if (ScheduledLinkedListNode.Index != -1)
332  throw new InvalidOperationException("MicroThread was already scheduled, something is probably wrong.");
333 
334  Schedule(scheduleMode);
335  }
336  }
337 
338  private void Schedule(ScheduleMode scheduleMode)
339  {
340  var nextCounter = Scheduler.SchedulerCounter++;
341  if (scheduleMode == ScheduleMode.First)
342  nextCounter = -nextCounter;
343 
344  schedulerCounter = nextCounter;
345 
346  Scheduler.scheduledMicroThreads.Enqueue(ScheduledLinkedListNode);
347  }
348 
349  internal void ThrowIfExceptionRequest()
350  {
351  if (ExceptionToRaise != null)
352  throw ExceptionToRaise;
353  }
354  }
355 }
A key to identify a specific profile.
Definition: ProfilingKey.cs:11
Represents an execution context managed by a Scheduler, that can cooperatively yield execution to ano...
Definition: MicroThread.cs:16
_In_ size_t _In_ DXGI_FORMAT _In_ size_t _In_ DXGI_FORMAT _In_ DWORD flags
Definition: DirectXTexP.h:170
MicroThread(Scheduler scheduler, MicroThreadFlags flags=MicroThreadFlags.None)
Definition: MicroThread.cs:74
void RaiseException(Exception e)
Raises an exception from within the MicroThread.
Definition: MicroThread.cs:280
Represents a container that can hold properties, lightweight to embed (lazy initialized).
PropertyContainer Tags
Gets the attached properties to this component.
Definition: MicroThread.cs:23
Flags
Enumeration of the new Assimp's flags.
async Task Run()
Yields to this MicroThread.
Definition: MicroThread.cs:265
Scheduler that manage a group of cooperating MicroThread.
Definition: Scheduler.cs:20
void Start(Func< Task > microThreadFunction, ScheduleMode scheduleMode=ScheduleMode.Last)
Starts this MicroThread with the specified function.
Definition: MicroThread.cs:211