SetCurrentScheduler() static private method

static private SetCurrentScheduler ( FiberScheduler scheduler, bool internalInvoke ) : void
scheduler FiberScheduler
internalInvoke bool
return void
Exemplo n.º 1
0
        /// <summary>
        /// Executes the fiber until it ends or yields.
        /// </summary>
        /// <returns>
        /// A fiber instruction to be processed by the scheduler.
        /// </returns>
        internal FiberInstruction Execute()
        {
            // Sanity check the scheduler. Since this is a scheduler
            // only issue, this test happens first before execution
            // and before allowing an exception handler to take over.
            if (FiberState == FiberState.Stopped)
            {
                throw new InvalidOperationException("An attempt was made to execute a stopped Fiber. This indicates a logic error in the scheduler.");
            }

            // Setup thread globals with this scheduler as the owner.
            // The sync context is also setup when the scheduler changes.
            // Setting the sync context isn't a simple assign in .NET
            // so don't do this until needed.
            //
            // Doing this setup in Execute() frees derived schedulers from the
            // burden of setting up the sync context or scheduler. It also allows the
            // current scheduler to change on demand when there is more than one
            // scheduler running per thread.
            //
            // For example, each MonoBehaviour in Unity is assigned its own
            // scheduler since the behavior determines the lifetime of tasks.
            // The active scheduler then can vary depending on which behavior is
            // currently executing tasks. Unity will decide outside of this framework
            // which behaviour to execute in which order and therefore which
            // scheduler is active. The active scheduler in this case can
            // only be determined at the time of fiber execution.
            //
            // Unlike CurrentFiber below, this does not need to be stacked
            // once set because the scheduler will never change during fiber
            // execution. Only something outside of the scheduler would
            // change the scheduler.
            if (FiberScheduler.Current != scheduler)
            {
                FiberScheduler.SetCurrentScheduler(scheduler, true);
            }

            // Push the current fiber onto the stack and pop it in finally.
            // This must be stacked because the fiber may spawn another
            // fiber which the scheduler may choose to inline which would result
            // in a new fiber temporarily taking its place as current.
            var lastFiber = Fiber.CurrentFiber;

            Fiber.CurrentFiber = this;

            try
            {
                // Process an abort if pending
                if (FiberState == FiberState.AbortRequested)
                {
                    throw new FiberAbortException();
                }

                object result;

                // Execute the coroutine or action
                if (coroutine != null)
                {
                    // Execute coroutine
                    if (coroutine.MoveNext())
                    {
                        // Get result of execution
                        result = coroutine.Current;
                    }
                    else
                    {
                        // Coroutine finished executing
                        result = Stop();
                    }
                }
                else if (action != null)
                {
                    // Execute action
                    action();

                    // Action finished executing
                    result = Stop();
                }
                else if (actionObject != null)
                {
                    // Execute action
                    actionObject(objectState);

                    // Action finished executing
                    result = Stop();
                }
                else if (func != null)
                {
                    result = func();
                    func   = null;
                }
                else if (funcObject != null)
                {
                    result     = funcObject(objectState);
                    funcObject = null;
                }
                else
                {
                    // Func execution nulls out the function
                    // so the scheduler will return to here
                    // when complete and then stop.
                    result = Stop();
                }

                // Treat null as a special case
                if (result == null)
                {
                    return(FiberInstruction.YieldToAnyFiber);
                }

                // Return instructions or throw if invalid
                var instruction = result as FiberInstruction;
                if (instruction == null)
                {
                    return(new ObjectInstruction(result));
                }
                else
                {
                    // Verify same scheduler
                    if (instruction is YieldUntilComplete && ((YieldUntilComplete)instruction).Fiber.Scheduler != FiberScheduler.Current)
                    {
                        throw new InvalidOperationException("Currently only fibers belonging to the same scheduler may be yielded to. FiberScheduler.Current = "
                                                            + (FiberScheduler.Current == null ? "null" : FiberScheduler.Current.ToString())
                                                            + ", Fiber.Scheduler = " + (((YieldUntilComplete)instruction).Fiber.Scheduler == null ? "null" : ((YieldUntilComplete)instruction).Fiber.Scheduler.ToString()));
                    }

                    var yieldToFiberInstruction = instruction as YieldToFiber;
                    if (yieldToFiberInstruction != null)
                    {
                        // Start fibers yielded to that aren't running yet
                        var originalState = (FiberState)Interlocked.CompareExchange(ref yieldToFiberInstruction.Fiber.fiberState, (int)FiberState.Running, (int)FiberState.Unstarted);
                        if (originalState == FiberState.Unstarted)
                        {
                            yieldToFiberInstruction.Fiber.scheduler = scheduler;
                        }

                        // Can't switch to stopped fibers
                        if (yieldToFiberInstruction.Fiber.FiberState == FiberState.Stopped)
                        {
                            throw new InvalidOperationException("An attempt was made to yield to a stopped fiber.");
                        }

                        // Verify scheduler
                        if (yieldToFiberInstruction.Fiber.Scheduler != FiberScheduler.Current)
                        {
                            throw new InvalidOperationException("Currently only fibers belonging to the same scheduler may be yielded to. FiberScheduler.Current = "
                                                                + (FiberScheduler.Current == null ? "null" : FiberScheduler.Current.ToString())
                                                                + ", Fiber.Scheduler = " + (yieldToFiberInstruction.Fiber.Scheduler == null ? "null" : yieldToFiberInstruction.Fiber.Scheduler.ToString()));
                        }
                    }

                    return(instruction);
                }
            }
            catch (Exception fiberException)
            {
                // If an exception occurs allow the handler to run.
                // Fiber execution will terminate even with a handler
                // because it's not possible to resume an action or a
                // coroutine that throws an exception.
                //
                // The only exception that could result in a contiunation
                // is FiberAbortException. Handlers may choose to deny
                // the abort by calling Fiber.ResetAbort().
                try
                {
                    // Invoke the exception handler (which could throw)
                    var unhandledException = this.unhandledException;
                    var eventArgs          = new FiberUnhandledExceptionEventArgs(this, fiberException);
                    if (unhandledException != null)
                    {
                        foreach (var subscriber in unhandledException.GetInvocationList())
                        {
                            subscriber.DynamicInvoke(this, eventArgs);
                            if (eventArgs.Handled)
                            {
                                break;
                            }
                        }
                    }

                    // See if an abort was requested. The flag is thread safe because
                    // it is only ever touched by the scheduler thread.
                    if (resetAbortRequested)
                    {
                        resetAbortRequested = false;
                        fiberState          = (int)FiberState.Running;
                        return(FiberInstruction.YieldToAnyFiber);                        // signal the scheduler to continue
                    }
                    else
                    {
                        if (!eventArgs.Handled)
                        {
                            throw fiberException;
                        }
                        else
                        {
                            return(Stop());
                        }
                    }
                }
                catch (Exception fiberOrHandlerException)
                {
                    // The exception wasn't from an abort or it wasn't reset
                    // and so the exception needs to happen.

                    // Clear reset requests (if any). This is needed here in case
                    // the exceptionHandler above threw after ResetAbort() was
                    // called.
                    //
                    // The flag is thread safe because
                    // it is only ever touched by the scheduler thread.
                    resetAbortRequested = false;

                    // Stop the fiber. This will invoke completion handlers
                    // but they won't know this fiber failed due to an exception
                    // unless they handled the exception above or wait for
                    // the scheduler to do something after the throw below.
                    Stop();

                    // Throw the exception back to the scheduler.
                    throw fiberOrHandlerException;
                }
            }
            finally
            {
                // Pop the current fiber
                Fiber.CurrentFiber = lastFiber;
            }
        }
Exemplo n.º 2
0
        /// <summary>
        /// Executes the fiber until it ends or yields.
        /// </summary>
        /// <returns>
        /// A fiber instruction to be processed by the scheduler.
        /// </returns>
        internal FiberInstruction Execute()
        {
            //Console.WriteLine ("Fiber {0}:{1} start executing", Id, Status);

            // Sanity check the scheduler. Since this is a scheduler
            // only issue, this test happens first before execution
            // and before allowing an exception handler to take over.
            if (IsCompleted)
            {
                throw new InvalidOperationException("An attempt was made to execute a completed Fiber. This indicates a logic error in the scheduler.");
            }

            // Setup thread globals with this scheduler as the owner.
            // The sync context is also setup when the scheduler changes.
            // Setting the sync context isn't a simple assign in .NET
            // so don't do this until needed.
            //
            // Doing this setup in Execute() frees derived schedulers from the
            // burden of setting up the sync context or scheduler. It also allows the
            // current scheduler to change on demand when there is more than one
            // scheduler running per thread.
            //
            // For example, each MonoBehaviour in Unity is assigned its own
            // scheduler since the behavior determines the lifetime of tasks.
            // The active scheduler then can vary depending on which behavior is
            // currently executing tasks. Unity will decide outside of this framework
            // which behaviour to execute in which order and therefore which
            // scheduler is active. The active scheduler in this case can
            // only be determined at the time of fiber execution.
            //
            // Unlike CurrentFiber below, this does not need to be stacked
            // once set because the scheduler will never change during fiber
            // execution. Only something outside of the scheduler would
            // change the scheduler.
            if (FiberScheduler.Current != scheduler)
            {
                FiberScheduler.SetCurrentScheduler(scheduler, true);
            }

            // Push the current fiber onto the stack and pop it in finally.
            // This must be stacked because the fiber may spawn another
            // fiber which the scheduler may choose to inline which would result
            // in a new fiber temporarily taking its place as current.
            var lastFiber = Fiber.CurrentFiber;

            Fiber.CurrentFiber = this;

            // Start the fiber if not started
            Interlocked.CompareExchange(ref status, (int)FiberStatus.WaitingToRun, (int)FiberStatus.Created);
            Interlocked.CompareExchange(ref status, (int)FiberStatus.Running, (int)FiberStatus.WaitingToRun);

            try {
                object result = null;

                // Execute the coroutine or action
                if (coroutine != null)
                {
                    //Console.WriteLine ("Fiber {0}:{1} start executing coroutine", Id, Status);
                    // Execute coroutine
                    if (coroutine.MoveNext())
                    {
                        // Get result of execution
                        result = coroutine.Current;

                        // If the coroutine returned a stop directly
                        // the fiber still needs to process it
                        if (result is StopInstruction)
                        {
                            Stop(FiberStatus.RanToCompletion);
                        }
                    }
                    else
                    {
                        // Coroutine finished executing
                        result = Stop(FiberStatus.RanToCompletion);
                    }
                    //Console.WriteLine ("Fiber {0}:{1} end executing coroutine", Id, Status);
                }
                else if (action != null)
                {
                    // Execute action
                    action();

                    // Action finished executing
                    result = Stop(FiberStatus.RanToCompletion);
                }
                else if (actionObject != null)
                {
                    // Execute action
                    actionObject(objectState);

                    // Action finished executing
                    result = Stop(FiberStatus.RanToCompletion);
                }
                else if (func != null)
                {
                    result = func();
                    func   = null;

                    if (result is StopInstruction)
                    {
                        Stop(FiberStatus.RanToCompletion);
                    }
                }
                else if (funcObject != null)
                {
                    result     = funcObject(objectState);
                    funcObject = null;

                    if (result is StopInstruction)
                    {
                        Stop(FiberStatus.RanToCompletion);
                    }
                }
                else
                {
                    // Func execution nulls out the function
                    // so the scheduler will return to here
                    // when complete and then stop.
                    result = Stop(FiberStatus.RanToCompletion);
                }

                // Treat null as a special case
                if (result == null)
                {
                    return(FiberInstruction.YieldToAnyFiber);
                }

                // Return instructions or throw if invalid
                var instruction = result as FiberInstruction;
                if (instruction == null)
                {
                    // If the result was an enumerator there is a nested coroutine to execute
                    if (result is IEnumerator)
                    {
                        instruction = new YieldUntilComplete(Fiber.Factory.StartNew(result as IEnumerator, cancelToken, scheduler));
                    }
                    else if (result is Fiber)
                    {
                        // Convert fibers into yield instructions
                        instruction = new YieldUntilComplete(result as Fiber);
                    }
                    else
                    {
                        // Pass through other values
                        return(new ObjectInstruction(result));
                    }
                }

                if (instruction is FiberResult)
                {
                    ResultAsObject = ((FiberResult)instruction).Result;
                    result         = Stop(FiberStatus.RanToCompletion);
                }

                // Verify same scheduler
                if (instruction is YieldUntilComplete && ((YieldUntilComplete)instruction).Fiber.Scheduler != FiberScheduler.Current)
                {
                    throw new InvalidOperationException("Currently only fibers belonging to the same scheduler may be yielded to. FiberScheduler.Current = "
                                                        + (FiberScheduler.Current == null ? "null" : FiberScheduler.Current.ToString())
                                                        + ", Fiber.Scheduler = " + (((YieldUntilComplete)instruction).Fiber.Scheduler == null ? "null" : ((YieldUntilComplete)instruction).Fiber.Scheduler.ToString()));
                }

                var yieldToFiberInstruction = instruction as YieldToFiber;
                if (yieldToFiberInstruction != null)
                {
                    // Start fibers yielded to that aren't running yet
                    Interlocked.CompareExchange(ref yieldToFiberInstruction.Fiber.status, (int)FiberStatus.WaitingToRun, (int)FiberStatus.Created);
                    var originalState = (FiberStatus)Interlocked.CompareExchange(ref yieldToFiberInstruction.Fiber.status, (int)FiberStatus.Running, (int)FiberStatus.WaitingToRun);
                    if (originalState == FiberStatus.WaitingToRun)
                    {
                        yieldToFiberInstruction.Fiber.scheduler = scheduler;
                    }

                    // Can't switch to completed fibers
                    if (yieldToFiberInstruction.Fiber.IsCompleted)
                    {
                        throw new InvalidOperationException("An attempt was made to yield to a completed fiber.");
                    }

                    // Verify scheduler
                    if (yieldToFiberInstruction.Fiber.Scheduler != FiberScheduler.Current)
                    {
                        throw new InvalidOperationException("Currently only fibers belonging to the same scheduler may be yielded to. FiberScheduler.Current = "
                                                            + (FiberScheduler.Current == null ? "null" : FiberScheduler.Current.ToString())
                                                            + ", Fiber.Scheduler = " + (yieldToFiberInstruction.Fiber.Scheduler == null ? "null" : yieldToFiberInstruction.Fiber.Scheduler.ToString()));
                    }
                }

                //Console.WriteLine ("Fiber {0}:{1} returning {2}", Id, Status, instruction);
                return(instruction);
            } catch (System.Threading.OperationCanceledException cancelException) {
                // Handle as proper cancellation only if the token matches.
                // Otherwise treat it as a fault.
                if (cancelException.CancellationToken == cancelToken)
                {
                    this.Exception = null;
                    return(Stop(FiberStatus.Canceled));
                }
                else
                {
                    this.Exception = cancelException;
                    return(Stop(FiberStatus.Faulted));
                }
            } catch (Exception fiberException) {
                this.Exception = fiberException;
                return(Stop(FiberStatus.Faulted));
            } finally {
                // Pop the current fiber
                Fiber.CurrentFiber = lastFiber;

                //Console.WriteLine ("Fiber {0}:{1} end executing", Id, Status);
            }
        }