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 
115  private IEnumerator ProcessFiberQueue()
116  {
117  Fiber fiber;
118  while(!fiberQueueCancelSource.IsCancellationRequested)
119  {
120  while(!fiberQueueCancelSource.IsCancellationRequested && fiberQueue.TryDequeue(out fiber))
121  StartUnityFiber(fiber);
122 
123  yield return null;
124  }
125  }
126 
140  private void StartUnityFiber(Fiber fiber)
141  {
142  Coroutine coroutine = behaviour.StartCoroutine(ExecuteFiberInternal(fiber));
143  fiber.Properties[UnityCoroutineKey] = coroutine;
144  }
145 
163  private IEnumerator ExecuteFiberInternal(Fiber fiber, bool singleStep = false, int fiberSwitchCount = 0)
164  {
165  FiberInstruction fiberInstruction = null;
166  bool ranOnce = false;
167 
168  while(!fiber.IsCompleted)
169  {
170  // If we are set to only advance one instruction then
171  // abort if we have already done that
172  if(singleStep && ranOnce)
173  yield break;
174  ranOnce = true;
175 
176  // Execute the fiber
177  fiberInstruction = ExecuteFiber(fiber);
178 
179  // Nothing more to do if stopped
180  if(fiberInstruction is StopInstruction)
181  yield break;
182 
183  // Not supported in Unity
184  if(fiberInstruction is YieldToFiber)
185  throw new InvalidOperationException("YieldToFiber is not supported by the Unity scheduler.");
186 
187  // Yield to any fiber means send null to the Unity scheduler
188  if(fiberInstruction is YieldToAnyFiber)
189  {
190  yield return null;
191  continue;
192  }
193 
194  // Pass back any objects directly to the Unity scheduler since
195  // these could be Unity scheduler commands
196  if(fiberInstruction is ObjectInstruction)
197  {
198  yield return ((ObjectInstruction)fiberInstruction).Value;
199  continue;
200  }
201 
202  // Convert framework wait instruction to Unity instruction
203  if(fiberInstruction is YieldForSeconds)
204  {
205  yield return new WaitForSeconds(((YieldForSeconds)fiberInstruction).Seconds);
206  continue;
207  }
208 
209  // Convert framework wait instruction to Unity instruction
210  if(fiberInstruction is YieldUntilComplete)
211  {
212  // Yield the coroutine that was stored when the instruction was started.
213  yield return ((YieldUntilComplete)fiberInstruction).Fiber.Properties[UnityCoroutineKey];
214  continue;
215  }
216  }
217  }
218 
225  public override string ToString()
226  {
227  if(SchedulerThread == Thread.CurrentThread)
228  return string.Format("[UnityFiberScheduler][{0}]", behaviour.ToString());
229  else
230  return base.ToString();
231  }
232 
233  private bool isDisposed = false;
234 
241  protected override void Dispose (bool disposing)
242  {
243  if(isDisposed)
244  return;
245 
246  if(disposing)
247  {
248  fiberQueueCancelSource.Cancel();
249  Fiber fiber;
250  while(fiberQueue.TryDequeue(out fiber));
251  }
252 
253  isDisposed = true;
254 
255  base.Dispose (disposing);
256  }
257  }
258 }
Schedules fibers for execution.
static UnityFiberScheduler Default
Gets the shared fiber scheduler instance.
A Fiber is a lightweight means of scheduling work that enables multiple units of processing to execut...
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.
Thread SchedulerThread
Gets the thread the scheduler is running on.
FiberScheduler that can execute fibers (yieldable coroutines) during the update cycle of a MonoBehavi...
FiberInstruction ExecuteFiber(Fiber fiber)
Executes the fiber until it ends or yields.
override void QueueFiber(Fiber fiber)
Queues the fiber for execution on the scheduler.
bool AllowInlining
Gets or sets a value indicating whether this SpicyPixel.Threading.FiberScheduler allows inlining.
UnityFiberScheduler(MonoBehaviour behaviour)
Initializes a new instance of the SpicyPixel.Threading.UnityFiberScheduler class.