コード例 #1
0
    /// <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);
    }
コード例 #2
0
    /// <summary>
    /// Registers that a given routine should be stopped as soon as possible.
    /// <para />
    /// Note that this will not take effect until the next time the routine yields or is set to be continued.
    /// Routines doing lengthy asynchronous work will not be halted mid-execution, and should yield return null occasionally to accomodate being stopped.
    /// </summary>
    /// <param name="handle">The handle of the routine to stop.</param>
    public static void StopRoutine(AsyncRoutineHandle handle)
    {
        Debug.Assert(handle != null, "Handle argument is null");

        lock (Async.ToStopRoutinesLock)
        {
            Async.ToStopRoutines.Add(handle.Routine);
        }
    }
コード例 #3
0
    /// <summary>
    /// Gets the handle of a routine, or creates one if one doesn't already exist.
    /// </summary>
    /// <param name="routine">The routine to create a handle for.</param>
    /// <returns>The handle of the routine.</returns>
    private static AsyncRoutineHandle GetOrCreateHandle(IEnumerator routine)
    {
        AsyncRoutineHandle handle;

        lock (Async.HandleLock)
        {
            Async.RoutineToHandleMap.TryGetValue(routine, out handle);

            if (handle == null)
            {
                handle = new AsyncRoutineHandle(routine);
                Async.RoutineToHandleMap.Add(routine, handle);
            }
        }

        return(handle);
    }
コード例 #4
0
 public WaitingForRoutineRoutine(IEnumerator routine, AsyncRoutineHandle waitingForHandle)
 {
     this.Routine          = routine;
     this.WaitingForHandle = waitingForHandle;
     this.WaitingContext   = Async.Context;
 }
コード例 #5
0
    /// <summary>
    /// Advances a routine by one step and processes the yielded value.
    ///
    /// This method also checks if the routine should be stopped, and
    /// removes stopped, errored and finished routines from the handle map.
    /// </summary>
    /// <param name="routine">The routine to advance.</param>
    /// <param name="currentContext">The current context.</param>
    /// <param name="allowThrottling">Whether to allow throttling the routine.</param>
    private static void AdvanceRoutine(IEnumerator routine, ExecutionContext currentContext, bool allowThrottling)
    {
        bool movedNext = false;
        bool stopped   = false;

        if (allowThrottling && currentContext == ExecutionContext.Game && Async.ThrottleGameLoopExecutionTime && Async.FrameTimeSpent >= Async.ExecutionTimePerFrame)
        {
            Async.QueueGameWork(routine);
            return;
        }

        lock (Async.ToStopRoutinesLock)
        {
            if (Async.ToStopRoutines.Remove(routine))
            {
                stopped = true;
            }
        }

        if (!stopped)
        {
            try
            {
                movedNext = routine.MoveNext();
            }
            catch (Exception ex)
            {
                Debug.LogException(ex);
                movedNext = false;
            }
        }

        // Also check whether we should stop it after the routine has finished its current step
        // This is important for lengthy async routines
        lock (Async.ToStopRoutinesLock)
        {
            if (Async.ToStopRoutines.Remove(routine))
            {
                movedNext = false;
                stopped   = true;
            }
        }

        if (movedNext)
        {
            object current = routine.Current;

            // Now we try to figure out what to do next, from the value that was yielded
            // This is just a long, ugly chain of if-else's

            if (object.ReferenceEquals(current, null))
            {
                if (Async.Context == ExecutionContext.Game)
                {
                    Async.QueueGameWork(routine);
                }
                else
                {
                    Async.QueueAsyncWork(routine);
                }
            }
            else
            {
                var instruction = current as IAsyncInstruction;

                if (instruction != null)
                {
                    instruction.Execute(routine);
                }
                else
                {
                    AsyncRoutineHandle waitForHandle = current as AsyncRoutineHandle;

                    if (waitForHandle == null)
                    {
                        var waitForRoutine = current as IEnumerator;

                        if (waitForRoutine != null)
                        {
                            waitForHandle = Async.GetHandle(waitForRoutine);

                            if (waitForHandle == null)
                            {
                                // It wasn't already a routine; run it
                                waitForHandle = Async.Run(waitForRoutine, currentContext);
                            }
                        }
                    }

                    if (waitForHandle != null)
                    {
                        lock (Async.WaitingForRoutineRoutinesLock)
                        {
                            Async.WaitingForRoutineRoutines.Add(new WaitingForRoutineRoutine(routine, waitForHandle));
                        }
                    }
                    else
                    {
                        var  unityYieldInstruction = current as YieldInstruction;
                        bool succeeded             = false;

                        if (unityYieldInstruction != null)
                        {
                            if (current is UnityEngine.WaitForSeconds)
                            {
                                if (Async.UnityWaitForSecondsField == null)
                                {
                                    Debug.LogError("Could not find Unity field 'WaitForSeconds.m_Seconds'; therefore you cannot currently yield Unity's WaitForSeconds class in AsyncRoutines. Use 'yield return new Async.WaitForSeconds(time)' instead.");
                                }
                                else
                                {
                                    var waitForSeconds = new Async.WaitForSeconds((float)Async.UnityWaitForSecondsField.GetValue(current));
                                    (waitForSeconds as IAsyncInstruction).Execute(routine);
                                    succeeded = true;
                                }
                            }
                            else if (current is WaitForFixedUpdate)
                            {
                                lock (Async.WaitingForFixedUpdateRoutinesLock)
                                {
                                    Async.WaitingForFixedUpdateRoutines.Add(routine);
                                }

                                succeeded = true;
                            }
                            else if (current is WaitForEndOfFrame)
                            {
                                if (currentContext == ExecutionContext.Async)
                                {
                                    Debug.LogError("Cannot wait for end of frame from an async context.");
                                }
                                else
                                {
                                    // Start a standard Unity coroutine to wait for end of frame.
                                    // It's the only way.
                                    Async.Instance.StartCoroutine(Async.Instance.WaitForEndOfFrameCoroutine(routine, (WaitForEndOfFrame)current));
                                }

                                succeeded = true;
                            }
                        }

                        if (!succeeded)
                        {
                            throw new InvalidAsyncInstructionException(current, routine);
                        }
                    }
                }
            }
        }
        else
        {
            // It's finished; remove it from the map
            lock (Async.HandleLock)
            {
                Async.RoutineToHandleMap.Remove(routine);
            }
        }
    }