UnityFiberScheduler.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;
31 using System.Threading;
32 using UnityEngine;
33 
34 namespace SpicyPixel.Threading
35 {
40  public sealed class UnityFiberScheduler : FiberScheduler
41  {
42  internal const string UnityCoroutineKey = "spicypixel.threading.unity.coroutine";
43 
44  private static readonly UnityFiberScheduler instance = new UnityFiberScheduler(ConcurrentBehaviour.SharedInstance);
45 
50  public static UnityFiberScheduler Default {
51  get {
52  return instance;
53  }
54  }
55 
59  private MonoBehaviour behaviour;
60 
65  private ConcurrentQueue<Fiber> fiberQueue = new ConcurrentQueue<Fiber>();
66 
70  private CancellationTokenSource fiberQueueCancelSource = new CancellationTokenSource();
71 
78  public UnityFiberScheduler(MonoBehaviour behaviour)
79  {
80  this.behaviour = behaviour;
81  behaviour.StartCoroutine(ProcessFiberQueue());
82  }
83 
94  protected override void QueueFiber(Fiber fiber)
95  {
96  if(isDisposed)
97  throw new ObjectDisposedException(GetType().FullName);
98 
99  // Coroutines are always inlined up to their
100  // first yield, so enqueuing here likely doesn't do
101  // anything other than delay a frame. But, it does
102  // ensure the correct thread.
103  if(AllowInlining && SchedulerThread == Thread.CurrentThread)
104  StartUnityFiber(fiber);
105  else
106  fiberQueue.Enqueue(fiber);
107  }
108 
120  protected override void AbortRequested(Fiber fiber)
121  {
122  }
123 
130  private IEnumerator ProcessFiberQueue()
131  {
132  Fiber fiber;
133  while(!fiberQueueCancelSource.IsCancellationRequested)
134  {
135  while(!fiberQueueCancelSource.IsCancellationRequested && fiberQueue.TryDequeue(out fiber))
136  StartUnityFiber(fiber);
137 
138  yield return null;
139  }
140  }
141 
155  private void StartUnityFiber(Fiber fiber)
156  {
157  Coroutine coroutine = behaviour.StartCoroutine(ExecuteFiberInternal(fiber));
158  fiber.Properties[UnityCoroutineKey] = coroutine;
159  }
160 
178  private IEnumerator ExecuteFiberInternal(Fiber fiber, bool singleStep = false, int fiberSwitchCount = 0)
179  {
180  FiberInstruction fiberInstruction = null;
181  bool ranOnce = false;
182 
183  while(!fiber.IsCompleted)
184  {
185  // If we are set to only advance one instruction then
186  // abort if we have already done that
187  if(singleStep && ranOnce)
188  yield break;
189  ranOnce = true;
190 
191  // Execute the fiber
192  fiberInstruction = ExecuteFiber(fiber);
193 
194  // Nothing more to do if stopped
195  if(fiberInstruction is StopInstruction)
196  yield break;
197 
198  // Not supported in Unity
199  if(fiberInstruction is YieldToFiber)
200  throw new InvalidOperationException("YieldToFiber is not supported by the Unity scheduler.");
201 
202  // Yield to any fiber means send null to the Unity scheduler
203  if(fiberInstruction is YieldToAnyFiber)
204  {
205  yield return null;
206  continue;
207  }
208 
209  // Pass back any objects directly to the Unity scheduler since
210  // these could be Unity scheduler commands
211  if(fiberInstruction is ObjectInstruction)
212  {
213  yield return ((ObjectInstruction)fiberInstruction).Value;
214  continue;
215  }
216 
217  // Convert framework wait instruction to Unity instruction
218  if(fiberInstruction is YieldForSeconds)
219  {
220  yield return new WaitForSeconds(((YieldForSeconds)fiberInstruction).Seconds);
221  continue;
222  }
223 
224  // Convert framework wait instruction to Unity instruction
225  if(fiberInstruction is YieldUntilComplete)
226  {
227  // Yield the coroutine that was stored when the instruction was started.
228  yield return ((YieldUntilComplete)fiberInstruction).Fiber.Properties[UnityCoroutineKey];
229  continue;
230  }
231  }
232  }
233 
240  public override string ToString()
241  {
242  if(SchedulerThread == Thread.CurrentThread)
243  return string.Format("[UnityFiberScheduler][{0}]", behaviour.ToString());
244  else
245  return base.ToString();
246  }
247 
248  private bool isDisposed = false;
249 
256  protected override void Dispose (bool disposing)
257  {
258  if(isDisposed)
259  return;
260 
261  if(disposing)
262  {
263  fiberQueueCancelSource.Cancel();
264  Fiber fiber;
265  while(fiberQueue.TryDequeue(out fiber));
266  }
267 
268  isDisposed = true;
269 
270  base.Dispose (disposing);
271  }
272  }
273 }
Schedules fibers for execution.
Yield execution until the watched fiber on the same scheduler is complete.
Wraps an object as an instruction
override void AbortRequested(Fiber fiber)
Invoked when an abort has been requested.
A Fiber is a lightweight means of scheduling work that enables multiple units of processing to execut...
Represents a fiber instruction to be processed by a FiberScheduler.
IDictionary< string, object > Properties
Gets user-defined properties associated with the fiber.
Definition: Fiber.cs:150
static ConcurrentBehaviour SharedInstance
Gets the shared instance valid for the lifetime of the application.
override string ToString()
Returns a System.String that represents the current SpicyPixel.Threading.UnityFiberScheduler.
Convenience class that extends MonoBehavior to provide a Scheduler and TaskFactory for executing task...
override void Dispose(bool disposing)
Dispose the scheduler.
Yield execution to a specific fiber belonging to the same scheduler as the current fiber...
Definition: YieldToFiber.cs:35
FiberScheduler that can execute fibers (yieldable coroutines) during the update cycle of a MonoBehavi...
A FiberInstruction to pause execution of a fiber for the specified duration.
An instruction to terminate execution of the current fiber.
override void QueueFiber(Fiber fiber)
Queues the fiber for execution on the scheduler.
bool IsCompleted
Gets a value indicating whether this instance is completed.
Definition: Fiber.cs:204
UnityFiberScheduler(MonoBehaviour behaviour)
Initializes a new instance of the SpicyPixel.Threading.UnityFiberScheduler class. ...
An instruction to yield execution to any fiber.