Represents a fiber instruction to be processed by a FiberScheduler.
Specific instructions understood by a scheduler are to be derived from this abstract type.
        private void OnFiberInstruction(Fiber fiber, FiberInstruction instruction, out bool fiberQueued, out Fiber nextFiber)
        {
            fiberQueued = false;
            nextFiber = null;

            YieldUntilComplete yieldUntilComplete = instruction as YieldUntilComplete;
            if(yieldUntilComplete != null)
            {
                // The additional complexity below is because this was going
                // to handle waiting for completions for fibers from other threads.
                // Currently fibers must belong to the same thread and this is enforced
                // by the instructions themselves for now.

                int completeOnce = 0;

                // FIXME: If we support multiple schedulers in the future
                // this callback could occur from another thread and
                // therefore after Dispose(). Would probably need a lock.

                // Watch for completion
                EventHandler<EventArgs> completed;
                completed = (sender, e) =>
                {
                    var originalCompleteOnce = Interlocked.CompareExchange(ref completeOnce, 1, 0);
                    if(originalCompleteOnce != 0)
                        return;

                    yieldUntilComplete.Fiber.Completed -= completed;
                    //QueueFiberForExecution(fiber);
                    QueueFiber(fiber); // optionally execute inline when the completion occurs
                };
                yieldUntilComplete.Fiber.Completed += completed;

                // If the watched fiber is already complete then continue immediately
                if(yieldUntilComplete.Fiber.FiberState == FiberState.Stopped)
                    completed(yieldUntilComplete.Fiber, EventArgs.Empty);

                fiberQueued = true;
                return;
            }

            YieldForSeconds yieldForSeconds = instruction as YieldForSeconds;
            if(yieldForSeconds != null)
            {
                QueueFiberForSleep(fiber, currentTime + yieldForSeconds.Seconds);
                fiberQueued = true;
                return;
            }

            YieldToFiber yieldToFiber = instruction as YieldToFiber;
            if(yieldToFiber != null)
            {
                RemoveFiberFromQueues(yieldToFiber.Fiber);
                nextFiber = yieldToFiber.Fiber;
                fiberQueued = false;
                return;
            }
        }
        private void OnFiberInstruction(Fiber fiber, FiberInstruction instruction, out bool fiberQueued, out Fiber nextFiber)
        {
            fiberQueued = false;
            nextFiber   = null;

            YieldUntilComplete yieldUntilComplete = instruction as YieldUntilComplete;

            if (yieldUntilComplete != null)
            {
                // The additional complexity below is because this was going
                // to handle waiting for completions for fibers from other threads.
                // Currently fibers must belong to the same thread and this is enforced
                // by the instructions themselves for now.

                int completeOnce = 0;

                // FIXME: If we support multiple schedulers in the future
                // this callback could occur from another thread and
                // therefore after Dispose(). Would probably need a lock.

                // Watch for completion
                EventHandler <EventArgs> completed;
                completed = (sender, e) =>
                {
                    var originalCompleteOnce = Interlocked.CompareExchange(ref completeOnce, 1, 0);
                    if (originalCompleteOnce != 0)
                    {
                        return;
                    }

                    yieldUntilComplete.Fiber.Completed -= completed;
                    //QueueFiberForExecution(fiber);
                    QueueFiber(fiber);                     // optionally execute inline when the completion occurs
                };
                yieldUntilComplete.Fiber.Completed += completed;

                // If the watched fiber is already complete then continue immediately
                if (yieldUntilComplete.Fiber.FiberState == FiberState.Stopped)
                {
                    completed(yieldUntilComplete.Fiber, EventArgs.Empty);
                }

                fiberQueued = true;
                return;
            }

            YieldForSeconds yieldForSeconds = instruction as YieldForSeconds;

            if (yieldForSeconds != null)
            {
                QueueFiberForSleep(fiber, currentTime + yieldForSeconds.Seconds);
                fiberQueued = true;
                return;
            }

            YieldToFiber yieldToFiber = instruction as YieldToFiber;

            if (yieldToFiber != null)
            {
                RemoveFiberFromQueues(yieldToFiber.Fiber);
                nextFiber   = yieldToFiber.Fiber;
                fiberQueued = false;
                return;
            }
        }
        private void OnFiberInstruction(Fiber fiber, FiberInstruction instruction, out bool fiberQueued, out Fiber nextFiber)
        {
            fiberQueued = false;
            nextFiber = null;

            YieldUntilComplete yieldUntilComplete = instruction as YieldUntilComplete;
            if (yieldUntilComplete != null) {
                // The additional complexity below is because this was going
                // to handle waiting for completions for fibers from other threads.
                // Currently fibers must belong to the same thread and this is enforced
                // by the instructions themselves for now.

                int completeOnce = 0;

                // FIXME: If we support multiple schedulers in the future
                // this callback could occur from another thread and
                // therefore after Dispose(). Would probably need a lock.

                yieldUntilComplete.Fiber.ContinueWith ((f) => {
                    var originalCompleteOnce = Interlocked.CompareExchange (ref completeOnce, 1, 0);
                    if (originalCompleteOnce != 0)
                        return;

                    QueueFiber (fiber); // optionally execute inline when the completion occurs

                    // If f.Status != RanToCompletion then this fiber needs to transition to the same state
                    // or faults won't propegate
                    //if (f.Status != FiberStatus.RanToCompletion) {
                    //    if (f.IsCanceled) {
                    //        if (f.CancellationToken == fiber.CancellationToken) {
                    //            fiber.CancelContinuation ();
                    //        } else {
                    //            fiber.FaultContinuation (new System.Threading.OperationCanceledException ());
                    //        }
                    //    } else if (f.IsFaulted) {
                    //        fiber.FaultContinuation (f.Exception);
                    //    }
                    //    RemoveFiberFromQueues (fiber);
                    //} else {
                    //    QueueFiber (fiber); // optionally execute inline when the completion occurs
                    //}
                });

                fiberQueued = true;
                return;
            }

            YieldForSeconds yieldForSeconds = instruction as YieldForSeconds;
            if (yieldForSeconds != null) {
                QueueFiberForSleep (fiber, currentTime + yieldForSeconds.Seconds);
                fiberQueued = true;
                return;
            }

            YieldToFiber yieldToFiber = instruction as YieldToFiber;
            if (yieldToFiber != null) {
                RemoveFiberFromQueues (yieldToFiber.Fiber);
                nextFiber = yieldToFiber.Fiber;
                fiberQueued = false;
                return;
            }
        }
 /// <summary>
 /// Creates a new task and starts executing it.
 /// </summary>
 /// <returns>
 /// The new executing task.
 /// </returns>
 /// <param name='taskFactory'>
 /// Task factory to start with.
 /// </param>
 /// <param name='instruction'>
 /// The instruction to start.
 /// </param>
 /// <param name='cancellationToken'>
 /// Cancellation token.
 /// </param>
 /// <param name='creationOptions'>
 /// Creation options.
 /// </param>
 /// <param name='scheduler'>
 /// Scheduler.
 /// </param>
 public static Task StartNew(this TaskFactory taskFactory, FiberInstruction instruction, CancellationToken cancellationToken, TaskCreationOptions creationOptions, TaskScheduler scheduler)
 {
     var task = new YieldableTask (instruction, cancellationToken, creationOptions);
     task.Start (scheduler);
     return task;
 }
        private void OnFiberInstruction(Fiber fiber, FiberInstruction instruction, out bool fiberQueued, out Fiber nextFiber)
        {
            fiberQueued = false;
            nextFiber   = null;

            YieldUntilComplete yieldUntilComplete = instruction as YieldUntilComplete;

            if (yieldUntilComplete != null)
            {
                // The additional complexity below is because this was going
                // to handle waiting for completions for fibers from other threads.
                // Currently fibers must belong to the same thread and this is enforced
                // by the instructions themselves for now.

                int completeOnce = 0;

                // FIXME: If we support multiple schedulers in the future
                // this callback could occur from another thread and
                // therefore after Dispose(). Would probably need a lock.

                yieldUntilComplete.Fiber.ContinueWith((f) => {
                    var originalCompleteOnce = Interlocked.CompareExchange(ref completeOnce, 1, 0);
                    if (originalCompleteOnce != 0)
                    {
                        return;
                    }

                    QueueFiber(fiber);  // optionally execute inline when the completion occurs

                    // If f.Status != RanToCompletion then this fiber needs to transition to the same state
                    // or faults won't propegate
                    //if (f.Status != FiberStatus.RanToCompletion) {
                    //    if (f.IsCanceled) {
                    //        if (f.CancellationToken == fiber.CancellationToken) {
                    //            fiber.CancelContinuation ();
                    //        } else {
                    //            fiber.FaultContinuation (new System.Threading.OperationCanceledException ());
                    //        }
                    //    } else if (f.IsFaulted) {
                    //        fiber.FaultContinuation (f.Exception);
                    //    }
                    //    RemoveFiberFromQueues (fiber);
                    //} else {
                    //    QueueFiber (fiber); // optionally execute inline when the completion occurs
                    //}
                });

                fiberQueued = true;
                return;
            }

            YieldForSeconds yieldForSeconds = instruction as YieldForSeconds;

            if (yieldForSeconds != null)
            {
                QueueFiberForSleep(fiber, currentTime + yieldForSeconds.Seconds);
                fiberQueued = true;
                return;
            }

            YieldToFiber yieldToFiber = instruction as YieldToFiber;

            if (yieldToFiber != null)
            {
                RemoveFiberFromQueues(yieldToFiber.Fiber);
                nextFiber   = yieldToFiber.Fiber;
                fiberQueued = false;
                return;
            }
        }
        /// <summary>
        /// Continues the task with a coroutine.
        /// </summary>
        /// <returns>
        /// The continued task.
        /// </returns>
        /// <param name='task'>
        /// Task to continue.
        /// </param>
        /// <param name='instruction'>
        /// The instruction to continue with.
        /// </param>
        /// <param name='cancellationToken'>
        /// Cancellation token.
        /// </param>
        /// <param name='continuationOptions'>
        /// Continuation options.
        /// </param>
        /// <param name='scheduler'>
        /// Scheduler to use when scheduling the task.
        /// </param>
        public static Task ContinueWith(this Task task, FiberInstruction instruction, CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
        {
            if (instruction == null)
                throw new ArgumentNullException ("instruction");
            if (scheduler == null)
                throw new ArgumentNullException ("scheduler");
            if (!(scheduler is FiberTaskScheduler))
                throw new ArgumentException ("The scheduler for a YieldableTask must be a FiberTaskScheduler", "scheduler");

            // This creates a continuation that runs on the default scheduler (e.g. ThreadPool)
            // where it's OK to wait on a child task to complete. The child task is scheduled
            // on the given scheduler and attached to the parent.

            //var outerScheduler = TaskScheduler.Current;
            //if(outerScheduler is MonoBehaviourTaskScheduler)
            //  outerScheduler = TaskScheduler.Default;

            var outerScheduler = TaskScheduler.Default;

            return task.ContinueWith((Task antecedent) => {
                var yieldableTask = new YieldableTask(instruction, cancellationToken, TaskCreationOptions.AttachedToParent);
                yieldableTask.Start(scheduler);
            }, cancellationToken, continuationOptions, outerScheduler);
        }
 /// <summary>
 /// Creates a new task and starts executing it.
 /// </summary>
 /// <returns>
 /// The new executing task.
 /// </returns>
 /// <param name='taskFactory'>
 /// Task factory to start with.
 /// </param>
 /// <param name='instruction'>
 /// The instruction to start.
 /// </param>
 /// <param name='creationOptions'>
 /// Creation options.
 /// </param>
 public static Task StartNew(this TaskFactory taskFactory, FiberInstruction instruction, TaskCreationOptions creationOptions)
 {
     return StartNew (taskFactory, instruction, taskFactory.CancellationToken, creationOptions, taskFactory.Scheduler);
 }
 /// <summary>
 /// Continues the task with a coroutine.
 /// </summary>
 /// <returns>
 /// The continued task.
 /// </returns>
 /// <param name='task'>
 /// Task to continue.
 /// </param>
 /// <param name='instruction'>
 /// The instruction to continue with.
 /// </param>
 /// <param name='scheduler'>
 /// Scheduler.
 /// </param>
 public static Task ContinueWith(this Task task, FiberInstruction instruction, TaskScheduler scheduler)
 {
     return ContinueWith (task, instruction, CancellationToken.None, TaskContinuationOptions.None, scheduler);
 }
 /// <summary>
 /// Continues the task with a coroutine.
 /// </summary>
 /// <returns>
 /// The continued task.
 /// </returns>
 /// <param name='task'>
 /// Task to continue.
 /// </param>
 /// <param name='instruction'>
 /// The instruction to continue with.
 /// </param>
 /// <param name='cancellationToken'>
 /// Cancellation token.
 /// </param>
 public static Task ContinueWith(this Task task, FiberInstruction instruction, CancellationToken cancellationToken)
 {
     return ContinueWith (task, instruction, cancellationToken, TaskContinuationOptions.None, TaskScheduler.Current);
 }
 /// <summary>
 /// Continues the task with a coroutine.
 /// </summary>
 /// <returns>
 /// The continued task.
 /// </returns>
 /// <param name='task'>
 /// Task to continue.
 /// </param>
 /// <param name='instruction'>
 /// The instruction to continue with.
 /// </param>
 public static Task ContinueWith(this Task task, FiberInstruction instruction)
 {
     return ContinueWith (task, instruction, TaskContinuationOptions.None);
 }
Example #11
0
        /// <summary>
        /// Wraps fiber execution to translate between framework and Unity concepts.
        /// </summary>
        /// <returns>
        /// A yield instruction that Unity will understand.
        /// </returns>
        /// <param name='fiber'>
        /// The fiber to execute.
        /// </param>
        /// <param name='singleStep'>
        /// If <c>true</c>, the method only executes a single step before breaking.
        /// This is used when switching between two fibers using <see cref="YieldToFiber"/>.
        /// </param>
        /// <param name='fiberSwitchCount'>
        /// This is the number of times a fiber switch has occured. 10 switches are
        /// allowed before unwinding in case Unity doesn't do this automatically.
        /// </param>
        private IEnumerator ExecuteFiberInternal(Fiber fiber, bool singleStep = false, int fiberSwitchCount = 0)
        {
            FiberInstruction fiberInstruction = null;
            bool             ranOnce          = false;

            while (!fiber.IsCompleted)
            {
                // If we are set to only advance one instruction then
                // abort if we have already done that
                if (singleStep && ranOnce)
                {
                    yield break;
                }
                ranOnce = true;

                // Execute the fiber
                fiberInstruction = ExecuteFiber(fiber);

                // Nothing more to do if stopped
                if (fiberInstruction is StopInstruction)
                {
                    yield break;
                }

                // Not supported in Unity
                if (fiberInstruction is YieldToFiber)
                {
                    throw new InvalidOperationException("YieldToFiber is not supported by the Unity scheduler.");
                }

                // Yield to any fiber means send null to the Unity scheduler
                if (fiberInstruction is YieldToAnyFiber)
                {
                    yield return(null);

                    continue;
                }

                // Pass back any objects directly to the Unity scheduler since
                // these could be Unity scheduler commands
                if (fiberInstruction is ObjectInstruction)
                {
                    yield return(((ObjectInstruction)fiberInstruction).Value);

                    continue;
                }

                // Convert framework wait instruction to Unity instruction
                if (fiberInstruction is YieldForSeconds)
                {
                    yield return(new WaitForSeconds(((YieldForSeconds)fiberInstruction).Seconds));

                    continue;
                }

                // Convert framework wait instruction to Unity instruction
                if (fiberInstruction is YieldUntilComplete)
                {
                    // Yield the coroutine that was stored when the instruction was started.
                    yield return(((YieldUntilComplete)fiberInstruction).Fiber.Properties[UnityCoroutineKey]);

                    continue;
                }
            }
        }
Example #12
0
        /// <summary>
        /// Wraps fiber execution to translate between framework and Unity concepts.
        /// </summary>
        /// <returns>
        /// A yield instruction that Unity will understand.
        /// </returns>
        /// <param name='fiber'>
        /// The fiber to execute.
        /// </param>
        /// <param name='singleStep'>
        /// If <c>true</c>, the method only executes a single step before breaking.
        /// This is used when switching between two fibers using <see cref="YieldToFiber"/>.
        /// </param>
        /// <param name='fiberSwitchCount'>
        /// This is the number of times a fiber switch has occured. 10 switches are
        /// allowed before unwinding in case Unity doesn't do this automatically.
        /// </param>
        private IEnumerator ExecuteFiberInternal(Fiber fiber, bool singleStep = false, int fiberSwitchCount = 0)
        {
            FiberInstruction fiberInstruction = null;
            bool             ranOnce          = false;

            while (fiber.IsAlive)
            {
                // If we are set to only advance one instruction then
                // abort if we have already done that
                if (singleStep && ranOnce)
                {
                    yield break;
                }
                ranOnce = true;

                try
                {
                    // Execute the fiber
                    fiberInstruction = ExecuteFiber(fiber);

                    // Nothing more to do if stopped
                    if (fiberInstruction is StopInstruction)
                    {
                        yield break;
                    }

                    // Not supported in Unity
                    if (fiberInstruction is YieldToFiber)
                    {
                        throw new InvalidOperationException("YieldToFiber is not supported by the Unity scheduler.");
                    }
                }
                catch (Exception ex)
                {
                    // Although this exception must result in the fiber
                    // being terminated, it does not have to result in the
                    // scheduler being brought down unless the exception
                    // handler rethrows the exception
                    if (!OnUnhandledException(fiber, ex))
                    {
                        throw ex;
                    }
                }

                // Yield to any fiber means send null to the Unity scheduler
                if (fiberInstruction is YieldToAnyFiber)
                {
                    yield return(null);

                    continue;
                }

                // Pass back any objects directly to the Unity scheduler since
                // these could be Unity scheduler commands
                if (fiberInstruction is ObjectInstruction)
                {
                    yield return(((ObjectInstruction)fiberInstruction).Value);

                    continue;
                }

                // Convert framework wait instruction to Unity instruction
                if (fiberInstruction is YieldForSeconds)
                {
                    yield return(new WaitForSeconds(((YieldForSeconds)fiberInstruction).Seconds));

                    continue;
                }

                // Convert framework wait instruction to Unity instruction
                if (fiberInstruction is YieldUntilComplete)
                {
                    // Yield the coroutine that was stored when the instruction was started.
                    yield return(((YieldUntilComplete)fiberInstruction).Fiber.Properties[UnityCoroutineKey]);

                    continue;
                }

#if false
                // Note: Tests with Unity show that StartCoroutine() always executes
                // inline and doesn't break recursion. The logic below doesn't work
                // because fibers that aren't already queued to the scheduler will
                // never finish executing becaues the YieldToAnyFiber case doesn't
                // requeue. Not sure it makes sense to support this in Unity anyway
                // since there is no easy way to remove scheduled work from the
                // scheduler once started. Wrapped cancellation token checks might
                // work, but the complexity isn't worth supporting.

                // Yield to a fiber means run and wait for one iteration of the fiber
                if (fiberInstruction is YieldToFiber)
                {
                    // If 2 fibers switch back and forth and Unity doesn't set recursion
                    // limits for inline execution then this will eventually fail. For
                    // now we allow a switch only 10 times before unwinding.
                    //
                    // Also note that StartUnityFiber() is not used here because we don't
                    // need to track the coroutine of this execution because it will never
                    // be waited on except here and because we need to pass some additional
                    // parameters to ExecuteFiberInternal.
                    if (fiberSwitchCount++ < 10)
                    {
                        Fiber yieldToFiber = ((YieldToFiber)fiberInstruction).Fiber;
                        if (yieldToFiber.FiberState != FiberState.Stopped)
                        {
                            yield return(behaviour.StartCoroutine(ExecuteFiberInternal(yieldToFiber, true, fiberSwitchCount)));
                        }
                        else
                        {
                            fiberSwitchCount = 0;
                            yield return(FiberInstruction.YieldToAnyFiber);
                        }
                    }
                    else
                    {
                        fiberSwitchCount = 0;
                        yield return(FiberInstruction.YieldToAnyFiber);
                    }

                    continue;
                }
#endif
            }
        }