Yieldable task for execution on a fiber.
Regular non-blocking tasks can also be scheduled on a FiberTaskScheduler, but yieldable tasks have the distinct ability to yield execution.
Inheritance: Task
        /// <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='coroutine'>
        /// The coroutine 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, IEnumerator coroutine, CancellationToken cancellationToken, TaskCreationOptions creationOptions, TaskScheduler scheduler)
        {
            var task = new YieldableTask(coroutine, cancellationToken, creationOptions);

            task.Start(scheduler);
            return(task);
        }
        /// <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);
        }
        /// <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;

            // The thread pool scheduler cannot be used in web scenarios. The outer scheduler
            // must be the fiber scheduler.
            //var outerScheduler = scheduler;

            return(task.ContinueWith((Task antecedent) => {
                var yieldableTask = new YieldableTask(instruction, cancellationToken, TaskCreationOptions.AttachedToParent);
                yieldableTask.Start(scheduler);
            }, cancellationToken, continuationOptions, outerScheduler));
        }
Example #4
0
        /// <summary>
        /// Execute the specified coroutine associated with a yieldable task.
        /// </summary>
        /// <remarks>
        /// Any exceptions that occur while executing the fiber will be
        /// associated with the specified task and rethrown by the framework.
        /// </remarks>
        /// <param name="task">
        /// The task associated with the executing fiber.
        /// </param>
        /// <returns>
        /// <see cref="FiberInstruction"/> or other scheduler specific instruction.
        /// </returns>
        private IEnumerator ExecuteYieldableTask(YieldableTask task)
        {
            object fiberResult = null;

            // TODO: Cleanup
            //
            // This uses internal setters to fake starting the fiber
            // and then uses internal Execute(). It should be possible to
            // use public APIs instead to:
            //   1. Set a fiber property to associate the task
            //   2. Set an exception handler on the FiberScheduler
            //   3. Retrieve the task in the handler from the fiber and set
            //      the exeption
            //   4. Return from the handler without rethrowing
            //
            // This method could be removed then and the internal setters
            // removed as well.
            task.Fiber.Scheduler = scheduler;
            task.Fiber.Status    = FiberStatus.Running;

            while (true)
            {
                try
                {
                    // Throw here because the scheduler is disposed and will not
                    // run anything else. No further access is valid.
                    CancellationToken.ThrowIfCancellationRequested();

                    fiberResult = task.Fiber.Execute();

                    if (fiberResult is StopInstruction)
                    {
                        // Check for exceptions
                        if (task.Fiber.IsFaulted)
                        {
                            task.FiberException = task.Fiber.Exception;
                        }

                        yield break;
                    }
                }
                catch (System.Threading.OperationCanceledException ex)
                {
                    // Fiber execution does not throw so the only exception
                    // expected is the cancellation above.
                    task.FiberException = ex;
                    yield break;
                }

                yield return(fiberResult);
            }
        }
        public void TestCancellationToken()
        {
            var start = DateTime.Now;

            var cancelSource = new CancellationTokenSource();
            var backgroundFiberScheduler = SystemFiberScheduler.StartNew(cancelSource.Token);

            // Submit a task to the background scheduler and wait for it to complete
            var task = new YieldableTask(new YieldForSeconds(2));
            task.RunSynchronously(new FiberTaskScheduler(backgroundFiberScheduler));

            // Shutdown the scheduler thread
            cancelSource.Cancel();
            backgroundFiberScheduler.SchedulerThread.Join(5000);

            var end = DateTime.Now;
            Assert.GreaterOrEqual(end - start, TimeSpan.FromSeconds(2));
            Assert.LessOrEqual(end - start, TimeSpan.FromSeconds(3));
        }
        /// <summary>
        /// Execute the specified coroutine associated with a yieldable task.
        /// </summary>
        /// <remarks>
        /// Any exceptions that occur while executing the fiber will be
        /// associated with the specified task and rethrown by the framework.
        /// </remarks>
        /// <param name="task">
        /// The task associated with the executing fiber.
        /// </param>
        /// <returns>
        /// <see cref="FiberInstruction"/> or other scheduler specific instruction.
        /// </returns>
        private IEnumerator ExecuteYieldableTask(YieldableTask task)
        {
            object fiberResult = null;

            // TODO: Cleanup
            //
            // This uses internal setters to fake starting the fiber
            // and then uses internal Execute(). It should be possible to
            // use public APIs instead to:
            //   1. Set a fiber property to associate the task
            //   2. Set an exception handler on the FiberScheduler
            //   3. Retrieve the task in the handler from the fiber and set
            //      the exeption
            //   4. Return from the handler without rethrowing
            //
            // This method could be removed then and the internal setters
            // removed as well.
            task.Fiber.Scheduler  = scheduler;
            task.Fiber.FiberState = FiberState.Running;

            while (true)
            {
                try
                {
                    cancelSource.Token.ThrowIfCancellationRequested();

                    fiberResult = task.Fiber.Execute();

                    if (fiberResult is StopInstruction)
                    {
                        yield break;
                    }
                }
                catch (Exception ex)
                {
                    task.FiberException = ex;
                    yield break;
                }

                yield return(fiberResult);
            }
        }
        /// <summary>
        /// Execute the specified coroutine associated with a yieldable task.
        /// </summary>
        /// <remarks>
        /// Any exceptions that occur while executing the fiber will be
        /// associated with the specified task and rethrown by the framework.
        /// </remarks>
        /// <param name="task">
        /// The task associated with the executing fiber.
        /// </param>
        /// <returns>
        /// <see cref="FiberInstruction"/> or other scheduler specific instruction.
        /// </returns>
        private IEnumerator ExecuteYieldableTask(YieldableTask task)
        {
            object fiberResult = null;

            // TODO: Cleanup
            //
            // This uses internal setters to fake starting the fiber
            // and then uses internal Execute(). It should be possible to
            // use public APIs instead to:
            //   1. Set a fiber property to associate the task
            //   2. Set an exception handler on the FiberScheduler
            //   3. Retrieve the task in the handler from the fiber and set
            //      the exeption
            //   4. Return from the handler without rethrowing
            //
            // This method could be removed then and the internal setters
            // removed as well.
            task.Fiber.Scheduler = scheduler;
            task.Fiber.FiberState = FiberState.Running;

            while(true)
            {
                try
                {
                    cancelSource.Token.ThrowIfCancellationRequested();

                    fiberResult = task.Fiber.Execute();

                    if(fiberResult is StopInstruction)
                        yield break;
                }
                catch(Exception ex)
                {
                    task.FiberException = ex;
                    yield break;
                }

                yield return fiberResult;
            }
        }
        /// <summary>
        /// Execute the specified coroutine associated with a yieldable task.
        /// </summary>
        /// <remarks>
        /// Any exceptions that occur while executing the fiber will be
        /// associated with the specified task and rethrown by the framework.
        /// </remarks>
        /// <param name="task">
        /// The task associated with the executing fiber.
        /// </param>
        /// <returns>
        /// <see cref="FiberInstruction"/> or other scheduler specific instruction.
        /// </returns>
        private IEnumerator ExecuteYieldableTask(YieldableTask task)
        {
            object fiberResult = null;

            // TODO: Cleanup
            //
            // This uses internal setters to fake starting the fiber
            // and then uses internal Execute(). It should be possible to
            // use public APIs instead to:
            //   1. Set a fiber property to associate the task
            //   2. Set an exception handler on the FiberScheduler
            //   3. Retrieve the task in the handler from the fiber and set
            //      the exeption
            //   4. Return from the handler without rethrowing
            //
            // This method could be removed then and the internal setters
            // removed as well.
            task.Fiber.Scheduler = scheduler;
            task.Fiber.Status = FiberStatus.Running;

            while(true)
            {
                try
                {
                    // Throw here because the scheduler is disposed and will not
                    // run anything else. No further access is valid.
                    CancellationToken.ThrowIfCancellationRequested();

                    fiberResult = task.Fiber.Execute();

                    if(fiberResult is StopInstruction) {
                        // Check for exceptions
                        if (task.Fiber.IsFaulted) {
                            task.FiberException = task.Fiber.Exception;
                        }

                        yield break;
                    }
                }
                catch(System.Threading.OperationCanceledException ex)
                {
                    // Fiber execution does not throw so the only exception
                    // expected is the cancellation above.
                    task.FiberException = ex;
                    yield break;
                }

                yield return fiberResult;
            }
        }
 /// <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;
 }
 /// <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='coroutine'>
 /// The coroutine 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, IEnumerator coroutine, CancellationToken cancellationToken, TaskCreationOptions creationOptions, TaskScheduler scheduler)
 {
     var task = new YieldableTask (coroutine, cancellationToken, creationOptions);
     task.Start (scheduler);
     return task;
 }
        /// <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);
        }
        private IEnumerator TestFuncTaskCoroutine()
        {
            var scheduler = new FiberTaskScheduler();

            var task = new YieldableTask(() => new YieldForSeconds(2));
            task.Start(scheduler);

            while (!task.IsCompleted)
                yield return FiberInstruction.YieldToAnyFiber;
        }
        public void TestInstructionInTask()
        {
            var start = DateTime.Now;

            using (var backgroundFiberScheduler = SystemFiberScheduler.StartNew()) {
                // Submit a task to the background scheduler and wait for it to complete
                var task = new YieldableTask(new YieldForSeconds(2));
                task.RunSynchronously(new FiberTaskScheduler(backgroundFiberScheduler));
            }

            var end = DateTime.Now;
            Assert.GreaterOrEqual(end - start, TimeSpan.FromSeconds(2));
            Assert.LessOrEqual(end - start, TimeSpan.FromSeconds(3));
        }