/// <summary> /// Starts an async routine in the given <see cref="ExecutionContext"/> and returns an <see cref="AsyncRoutineHandle"/> for that routine. /// </summary> /// <param name="routine">The enumerator to run as a routine.</param> /// <param name="context">The context to start the routine in.</param> /// <returns>A <see cref="AsyncRoutineHandle"/> for the routine.</returns> public static AsyncRoutineHandle Run(IEnumerator routine, ExecutionContext context) { Debug.Assert(routine != null, "Routine argument is null."); Debug.Assert(Async.GetHandle(routine) == null, "Routine to run is already an active async routine!"); Async.TryEnsureSingleInstance(); AsyncRoutineHandle handle = Async.GetOrCreateHandle(routine); switch (context) { case ExecutionContext.Game: Async.AdvanceRoutine(routine, ExecutionContext.Game, true); break; case ExecutionContext.Async: Async.QueueAsyncWork(routine); break; default: throw new NotImplementedException(context.ToString()); } // If the routine finished instantly, it might have been deleted from the handle map already. // This is okay. return(handle); }
/// <summary> /// Advances routines that are waiting for fixed update. /// </summary> private void FixedUpdate() { lock (Async.WaitingForFixedUpdateRoutinesLock) { for (int i = 0; i < Async.WaitingForFixedUpdateRoutines.Count; i++) { Async.AdvanceRoutine(Async.WaitingForFixedUpdateRoutines[i], ExecutionContext.Game, false); } Async.WaitingForFixedUpdateRoutines.Clear(); } }
/// <summary> /// A vanilla Unity coroutine that waits for end of frame, then advances an async routine that yielded <see cref="WaitForEndOfFrame"/>. /// </summary> private IEnumerator WaitForEndOfFrameCoroutine(IEnumerator routine, WaitForEndOfFrame waitInstance) { yield return(waitInstance); Async.AdvanceRoutine(routine, ExecutionContext.Game, false); }
/// <summary> /// Checks conditions for waiting routines to continue, and advances all game context async routines. /// </summary> private void Update() { lock (Async.GameListsLock) { // Switch active and inactive list, so we can iterate over the now inactive // list in peace while the now active list gathers up new requests until the // next frame. var temp = Async.GameListActive; Async.GameListActive = Async.GameListInactive; Async.GameListInactive = temp; } var list = Async.GameListInactive; // Handle routines that are waiting for other routines to complete // This code should run very fast as we're just performing a bunch of // dictionary lookups and removing items from a list. // // We can feel okay about holding the lock for the entire time. lock (Async.WaitingForRoutineRoutinesLock) { for (int i = 0; i < Async.WaitingForRoutineRoutines.Count; i++) { var waitingRoutine = Async.WaitingForRoutineRoutines[i]; if (Async.GetHandle(waitingRoutine.WaitingForHandle.Routine) == null) { // Routine it's waiting for doesn't exist any more; it can now continue. // Remove it from the waiting list and schedule it for execution again. Async.WaitingForRoutineRoutines.RemoveAt(i); i--; if (waitingRoutine.WaitingContext == ExecutionContext.Game) { list.Add(waitingRoutine.Routine); } else { Async.QueueAsyncWork(waitingRoutine.Routine); } } } } // Handle routines that are waiting for a certain condition to be fulfilled // We are likely just checking a few conditions; this should hopefully be // quite fast. // // We feel less okay about holding the lock for the entire time, but still okay enough. lock (Async.WaitingForConditionRoutinesLock) { for (int i = 0; i < Async.WaitingForConditionRoutines.Count; i++) { var waitingRoutine = Async.WaitingForConditionRoutines[i]; if (waitingRoutine.IsConditionFulfilled()) { // The waiting routine's condition has been fulfilled; it can now continue. // Remove it from the waiting list and schedule it for execution again. Async.WaitingForConditionRoutines.RemoveAt(i); i--; if (waitingRoutine.WaitingContext == ExecutionContext.Game) { list.Add(waitingRoutine.Routine); } else { Async.QueueAsyncWork(waitingRoutine.Routine); } } } } // We're about to begin executing the actual game update routines, so start the timer. lock (Async.FrameTimerLock) { Async.FrameTimer.Reset(); Async.FrameTimer.Start(); } for (int i = 0; i < list.Count; i++) { Async.AdvanceRoutine(list[i], ExecutionContext.Game, true); } // Clear the list to free completed routines for GC. // Routines that ought to be continued have been added to the currently active list. list.Clear(); }