FiberTaskScheduler.cs
Go to the documentation of this file.
1 /*
2 
3 Author: Aaron Oneal, http://aarononeal.info
4 
5 Copyright (c) 2012 Spicy Pixel, http://spicypixel.com
6 
7 Permission is hereby granted, free of charge, to any person obtaining
8 a copy of this software and associated documentation files (the
9 "Software"), to deal in the Software without restriction, including
10 without limitation the rights to use, copy, modify, merge, publish,
11 distribute, sublicense, and/or sell copies of the Software, and to
12 permit persons to whom the Software is furnished to do so, subject to
13 the following conditions:
14 
15 The above copyright notice and this permission notice shall be
16 included in all copies or substantial portions of the Software.
17 
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 
26 */
27 using System;
28 using System.Collections;
30 using System.Threading;
31 using System.Threading.Tasks;
32 
33 namespace SpicyPixel.Threading.Tasks
34 {
40  public sealed class FiberTaskScheduler : TaskScheduler, IDisposable
41  {
42  FiberScheduler scheduler;
43 
51  {
52  get { return scheduler.CancellationToken; }
53  }
54 
62  public FiberTaskScheduler () : this(FiberScheduler.Current)
63  {
64  }
65 
74  {
75  this.scheduler = scheduler == null ? FiberScheduler.Current : scheduler;
76  }
77 
85  {
86  get { return scheduler; }
87  }
88 
99  protected override void QueueTask (Task task)
100  {
101  // Start a fiber to run the task
102  Fiber.Factory.StartNew(ExecuteTask(task), scheduler);
103  }
104 
132  protected override bool TryExecuteTaskInline (Task task, bool taskWasPreviouslyQueued)
133  {
134  // 1. Tasks using fiber schedulers have thread affinity and must run on the thread the scheduler was created on.
135  // 2. Fiber tasks cannot run inline here because they may yield which is only handled when queued.
136  //
137  // That being said, the fiber scheduler may choose to inline anyway when
138  // queueing occurs on the scheduler thread, it just doesn't happen in this method.
139  if(scheduler.SchedulerThread != Thread.CurrentThread || task is YieldableTask)
140  return false;
141 
142  return TryExecuteTask(task);
143  }
144 
145  #region implemented abstract members of System.Threading.Tasks.TaskScheduler
146 
161  protected override bool TryDequeue (Task task)
162  {
163  return false;
164  }
165 
176  protected override IEnumerable<Task> GetScheduledTasks ()
177  {
178  return null;
179  }
180 
181  #endregion
182 
192  private IEnumerator ExecuteTask (Task task)
193  {
194  // Execute yieldable actions using the MonoBehavior.
195  //
196  // The continuation will execute the task itself
197  // via TryExecuteTask(). That task action will
198  // rethrow any exceptions caught during the yieldable
199  // action invocation and TryExecuteTask() will set
200  // the final state.
201  //
202  // The downside to this approach is that yieldable tasks
203  // aren't considered running until they have already
204  // completed because TryExecuteTask() is what sets state.
205  var yieldableTask = task as YieldableTask;
206  if(yieldableTask != null)
207  yield return Fiber.Factory.StartNew(ExecuteYieldableTask(yieldableTask), scheduler);
208 
209  // Run the action
210  TryExecuteTask(task);
211  }
212 
226  private IEnumerator ExecuteYieldableTask(YieldableTask task)
227  {
228  object fiberResult = null;
229 
230  // TODO: Cleanup
231  //
232  // This uses internal setters to fake starting the fiber
233  // and then uses internal Execute(). It should be possible to
234  // use public APIs instead to:
235  // 1. Set a fiber property to associate the task
236  // 2. Set an exception handler on the FiberScheduler
237  // 3. Retrieve the task in the handler from the fiber and set
238  // the exeption
239  // 4. Return from the handler without rethrowing
240  //
241  // This method could be removed then and the internal setters
242  // removed as well.
243  task.Fiber.Scheduler = scheduler;
244  task.Fiber.Status = FiberStatus.Running;
245 
246  while(true)
247  {
248  try
249  {
250  // Throw here because the scheduler is disposed and will not
251  // run anything else. No further access is valid.
253 
254  fiberResult = task.Fiber.Execute();
255 
256  if(fiberResult is StopInstruction) {
257  // Check for exceptions
258  if (task.Fiber.IsFaulted) {
259  task.FiberException = task.Fiber.Exception;
260  }
261 
262  yield break;
263  }
264  }
266  {
267  // Fiber execution does not throw so the only exception
268  // expected is the cancellation above.
269  task.FiberException = ex;
270  yield break;
271  }
272 
273  yield return fiberResult;
274  }
275  }
276 
277  #region IDisposable implementation
278 
284  {
285  Dispose(false);
286  }
287 
291  private bool isDisposed = false;
292 
304  public void Dispose()
305  {
306  Dispose(true);
307  GC.SuppressFinalize(this);
308  }
309 
320  private /*protected virtual*/ void Dispose(bool disposing)
321  {
322  // Do nothing if already called
323  if(isDisposed)
324  return;
325 
326  if(disposing)
327  {
328  // Free other state (managed objects).
329  scheduler.Dispose();
330  }
331 
332  // Free your own state (unmanaged objects).
333  // Set large fields to null.
334 
335  // Mark disposed
336  isDisposed = true;
337  }
338  #endregion
339  }
340 }
341 
Schedules fibers for execution.
override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
Tries to execute the task inline.
Fiber StartNew(IEnumerator coroutine)
Start executing a new fiber using the default scheduler on the thread.
Yieldable task for execution on a fiber.
static FiberFactory Factory
Gets the default factory for creating fibers.
Definition: Fiber.cs:120
A Fiber is a lightweight means of scheduling work that enables multiple units of processing to execut...
static FiberScheduler Current
Gets the default fiber scheduler for the thread.
void Dispose()
Releases all resource used by the SpicyPixel.Threading.Tasks.FiberTaskScheduler object.
FiberTaskScheduler()
Initializes a new instance of the SpicyPixel.Threading.Tasks.FiberTaskScheduler class.
An instruction to terminate execution of the current fiber.
FiberStatus Status
Gets or sets the state of the fiber.
Definition: Fiber.cs:238
bool IsFaulted
Gets a value indicating whether this instance is faulted.
Definition: Fiber.cs:217
override void QueueTask(Task task)
Queues a non-blocking task.
override bool TryDequeue(Task task)
Tries to dequeue a task.
FiberStatus
Represents the current state of a fiber.
Definition: FiberStatus.cs:34
FiberTaskScheduler(FiberScheduler scheduler)
Initializes a new instance of the SpicyPixel.Threading.Tasks.FiberTaskScheduler class.
override IEnumerable< Task > GetScheduledTasks()
For debugger support only, generates an enumerable of Task instances currently queued to the schedule...
Exception Exception
Gets the exception that led to the Faulted state.
Definition: Fiber.cs:227
TaskScheduler that can execute fibers (yieldable coroutines).