protected static IEnumerator CoroutinesRunner(ThreadSafeQueue <PausableTask> newTaskRoutines, FasterList <PausableTask> coroutines, FlushingOperation flushingOperation, RunningTasksInfo info, FlushTasksDel flushTaskDel, RunnerBehaviour runnerBehaviourForUnityCoroutine = null) { while (true) { if (flushingOperation.waitForflush == false) //don't start anything while flushing { flushTaskDel(newTaskRoutines, coroutines, flushingOperation); } info.count = coroutines.Count; for (int i = 0; i < info.count; i++) { var enumerator = coroutines[i]; try { //let's spend few words about this. Special YieldInstruction can be only processed internally //by Unity. The simplest way to handle them is to hand them to Unity itself. //However while the Unity routine is processed, the rest of the coroutine is waiting for it. //This would defeat the purpose of the parallel procedures. For this reason, the Parallel //routines will mark the enumerator returned as ParallelYield which will change the way the routine is processed. //in this case the MonoRunner won't wait for the Unity routine to continue processing the next tasks. var current = enumerator.Current; PausableTask enumeratorToHandle = null; var yield = current as ParallelYield; if (yield != null) { current = yield.Current; } else { enumeratorToHandle = enumerator; } if (runnerBehaviourForUnityCoroutine != null) { if (current is YieldInstruction || current is AsyncOperation) { runnerBehaviourForUnityCoroutine.StartCoroutine(HandItToUnity(current, enumeratorToHandle, newTaskRoutines, flushingOperation)); if (enumeratorToHandle != null) { coroutines.UnorderredRemoveAt(i--); info.count = coroutines.Count; continue; } } } bool result; #if TASKS_PROFILER_ENABLED && UNITY_EDITOR result = Tasks.Profiler.TaskProfiler.MonitorUpdateDuration(enumerator); #else result = enumerator.MoveNext(); #endif if (result == false) { var disposable = enumerator as IDisposable; if (disposable != null) { disposable.Dispose(); } coroutines.UnorderredRemoveAt(i--); } } catch (Exception e) { string message = "Coroutine Exception: "; Utility.Console.LogException(new CoroutineException(message, e)); coroutines.UnorderredRemoveAt(i--); } info.count = coroutines.Count; } if (flushingOperation.waitForflush == true && coroutines.Count == 0) { //this process is more complex than I like, not 100% sure it covers all the cases yet flushingOperation.waitForflush = false; flushingOperation.stopped = false; } yield return(null); } }
static IEnumerator HandItToUnity(object current, PausableTask task, ThreadSafeQueue <PausableTask> newTaskRoutines, FlushingOperation flushingOperation) { yield return(current); InternalThreadUnsafeStartCoroutine(task, newTaskRoutines, flushingOperation); }
internal static IEnumerator Process( ThreadSafeQueue <IPausableTask> newTaskRoutines, FasterList <IPausableTask> coroutines, FlushingOperation flushingOperation, RunningTasksInfo info, FlushTasksDel flushTaskDel, RunnerBehaviour runnerBehaviourForUnityCoroutine, Action <IPausableTask> resumeOperation) { while (true) { if (false == flushingOperation.stopped) //don't start anything while flushing { flushTaskDel(newTaskRoutines, coroutines, flushingOperation); } else if (runnerBehaviourForUnityCoroutine != null) { runnerBehaviourForUnityCoroutine.StopAllCoroutines(); } info.count = coroutines.Count; for (var i = 0; i < info.count; i++) { var pausableTask = coroutines[i]; //let's spend few words on this. //yielded YieldInstruction and AsyncOperation can //only be processed internally by Unity. //The simplest way to handle them is to hand them to Unity itself. //However while the Unity routine is processed, the rest of the //coroutine is waiting for it. This would defeat the purpose //of the parallel procedures. For this reason, a Parallel //task will mark the enumerator returned as ParallelYield which //will change the way the routine is processed. //in this case the MonoRunner won't wait for the Unity routine //to continue processing the next tasks. //Note that it is much better to return wrap AsyncOperation around //custom IEnumerator classes then returning them directly as //most of the time they don't need to be handled by Unity as //YieldInstructions do /// /// Handle special Unity instructions /// you should avoid them or wrap them /// around custom IEnumerator to avoid /// the cost of two allocations per instruction /// if (runnerBehaviourForUnityCoroutine != null && flushingOperation.stopped == false) { var current = pausableTask.Current; if (current is YieldInstruction) { var handItToUnity = new HandItToUnity (current, pausableTask, resumeOperation, flushingOperation); //remove the special instruction. it will //be added back once Unity completes. coroutines.UnorderedRemoveAt(i--); info.count = coroutines.Count; var coroutine = runnerBehaviourForUnityCoroutine.StartCoroutine (handItToUnity.GetEnumerator()); (pausableTask as PausableTask).onExplicitlyStopped = () => { runnerBehaviourForUnityCoroutine.StopCoroutine(coroutine); handItToUnity.ForceStop(); }; continue; } var parallelTask = (current as ParallelTaskCollection.ParallelTask); if (parallelTask != null && parallelTask.current is YieldInstruction) { var handItToUnity = new HandItToUnity(parallelTask.current); parallelTask.Add(handItToUnity.WaitUntilIsDone()); var coroutine = runnerBehaviourForUnityCoroutine.StartCoroutine (handItToUnity.GetEnumerator()); (pausableTask as PausableTask).onExplicitlyStopped = () => { runnerBehaviourForUnityCoroutine.StopCoroutine(coroutine); handItToUnity.ForceStop(); }; } } bool result; #if TASKS_PROFILER_ENABLED && UNITY_EDITOR result = TASK_PROFILER.MonitorUpdateDuration(pausableTask, info.runnerName); #else result = pausableTask.MoveNext(); #endif if (result == false) { var disposable = pausableTask as IDisposable; if (disposable != null) { disposable.Dispose(); } coroutines.UnorderedRemoveAt(i--); } info.count = coroutines.Count; } if (flushingOperation.stopped == true && coroutines.Count == 0) { //once all the coroutines are flushed //the loop can return accepting new tasks flushingOperation.stopped = false; } yield return(null); } }