/// <summary> /// Start executing a new fiber using the default scheduler on the thread. /// </summary> /// <returns> /// Returns a <see cref="Fiber"/> /// that can be yielded against to wait for the fiber to complete. /// </returns> /// <param name='action'> /// A non-blocking action to execute on the fiber. /// </param> /// <param name="cancellationToken">Cancellation token.</param> /// <param name='scheduler'> /// A scheduler to execute the fiber on. /// </param> public Fiber StartNew(Action action, CancellationToken cancellationToken, FiberScheduler scheduler) { var fiber = new Fiber(action, cancellationToken); fiber.Start(scheduler); return(fiber); }
static internal void SetCurrentScheduler(FiberScheduler scheduler, bool internalInvoke) { // Only allow scheduler changes on a thread when a fiber is not executing. // Ignore this check if this is an internal invocation such as from // Fiber.Execute() which needs to switch schedulers on demand. if (!internalInvoke && Fiber.CurrentFiber != null) { throw new InvalidOperationException("The current scheduler for the thread cannot be changed from inside an executing fiber."); } // Skip work if nothing to change if (currentScheduler == scheduler) { return; } // Assign the scheduler currentScheduler = scheduler; // Update the synchronization context if it changed if (currentScheduler != null && SynchronizationContext.Current != currentScheduler.SynchronizationContext) { SynchronizationContext.SetSynchronizationContext(currentScheduler.SynchronizationContext); } else if (currentScheduler == null && SynchronizationContext.Current != null) { SynchronizationContext.SetSynchronizationContext(null); } }
/// <summary> /// Creates a continuation that executes asynchronously when the target fiber completes. /// </summary> /// <returns>A fiber that executes when the target fiber completes.</returns> /// <param name="continuationCoroutine">Continuation coroutine.</param> /// <param name="cancellationToken">Cancellation token.</param> /// <param name="continuationOptions">Continuation options.</param> /// <param name="scheduler">Scheduler.</param> public Fiber ContinueWith(IEnumerator continuationCoroutine, CancellationToken cancellationToken, FiberContinuationOptions continuationOptions, FiberScheduler scheduler) { if (continuationCoroutine == null) throw new ArgumentNullException("continuationCoroutine"); if (scheduler == null) throw new ArgumentNullException("scheduler"); var fiber = new Fiber(continuationCoroutine, cancellationToken); fiber.antecedent = this; fiber.status = (int)FiberStatus.WaitingForActivation; var continuation = new FiberContinuation(fiber, continuationOptions, scheduler); if (IsCompleted) { continuation.Execute(); } else { // Lazy create queue if (continuations == null) continuations = new Queue<FiberContinuation>(); continuations.Enqueue(continuation); } return fiber; }
/// <summary> /// Crates a Fiber that waits for a delay before completing. /// </summary> /// <param name="millisecondsDelay">Milliseconds to delay.</param> /// <param name="cancellationToken">Cancellation token.</param> /// <param name="scheduler">Scheduler.</param> public static Fiber Delay(int millisecondsDelay, CancellationToken cancellationToken, FiberScheduler scheduler) { if (millisecondsDelay < -1) throw new ArgumentOutOfRangeException ("millisecondsDelay"); return Fiber.Factory.StartNew(DelayCoroutine(millisecondsDelay, cancellationToken), scheduler); }
/// <summary> /// Initializes the task factory during Awake(). /// </summary> /// <remarks> /// The task factory cannot be initialized in the constructor /// because it must be initialized from the coroutine /// execution thread which the constructor does not guarantee. /// </remarks> protected virtual void Awake() { _taskFactory = this.CreateTaskFactory(); _taskScheduler = _taskFactory.Scheduler; _fiberScheduler = ((FiberTaskScheduler)_taskScheduler).FiberScheduler; _fiberFactory = new FiberFactory(_fiberScheduler); }
/// <summary> /// Initializes a new instance of the <see cref="SpicyPixel.Threading.FiberFactory"/> class. /// </summary> /// <param name="cancellationToken">Cancellation token.</param> /// <param name="continuationOptions">Continuation options.</param> /// <param name="scheduler">Scheduler.</param> public FiberFactory(CancellationToken cancellationToken, FiberContinuationOptions continuationOptions, FiberScheduler scheduler) { this.cancellationToken = cancellationToken; this.continuationOptions = continuationOptions; this.scheduler = scheduler; CheckContinuationOptions (continuationOptions); }
/// <summary> /// Initializes a new instance of the <see cref="SpicyPixel.Threading.FiberFactory"/> class. /// </summary> /// <param name="cancellationToken">Cancellation token.</param> /// <param name="continuationOptions">Continuation options.</param> /// <param name="scheduler">Scheduler.</param> public FiberFactory(CancellationToken cancellationToken, FiberContinuationOptions continuationOptions, FiberScheduler scheduler) { this.cancellationToken = cancellationToken; this.continuationOptions = continuationOptions; this.scheduler = scheduler; CheckContinuationOptions(continuationOptions); }
/// <summary> /// Start executing the fiber using the specified scheduler. /// </summary> /// <remarks> /// This method is safe to call from any thread even if different /// than the scheduler execution thread. /// </remarks> /// <returns> /// Returns a <see cref="YieldUntilComplete"/> fiber instruction /// that can be yielded against to wait for the fiber to complete. /// </returns> /// <param name='scheduler'> /// The scheduler to start the fiber on. /// </param> public YieldUntilComplete Start(FiberScheduler scheduler) { // It would be unusual to attempt to start a Fiber more than once, // but to be safe and to support calling from any thread // use Interlocked with boxing on the enum. var originalState = (FiberState)Interlocked.CompareExchange(ref fiberState, (int)FiberState.Running, (int)FiberState.Unstarted); if (originalState != FiberState.Unstarted) { throw new InvalidOperationException("A fiber cannot be started again once it has begun running or has completed."); } this.scheduler = scheduler; ((IFiberScheduler)this.scheduler).QueueFiber(this); return(new YieldUntilComplete(this)); }
/// <summary> /// Start executing the fiber using the specified scheduler. /// </summary> /// <remarks> /// This method is safe to call from any thread even if different /// than the scheduler execution thread. /// </remarks> /// <param name='scheduler'> /// The scheduler to start the fiber on. /// </param> public void Start(FiberScheduler scheduler) { // It would be unusual to attempt to start a Fiber more than once, // but to be safe and to support calling from any thread // use Interlocked with boxing on the enum. var originalState = (FiberStatus)Interlocked.CompareExchange(ref status, (int)FiberStatus.WaitingToRun, (int)FiberStatus.Created); if (originalState != FiberStatus.Created) { originalState = (FiberStatus)Interlocked.CompareExchange(ref status, (int)FiberStatus.WaitingToRun, (int)FiberStatus.WaitingForActivation); if (originalState != FiberStatus.WaitingForActivation) { throw new InvalidOperationException("A fiber cannot be started again once it has begun running or has completed."); } } this.scheduler = scheduler; ((IFiberScheduler)this.scheduler).QueueFiber(this); }
/// <summary> /// Creates a continuation that executes asynchronously when the target fiber completes. /// </summary> /// <returns>A fiber that executes when the target fiber completes.</returns> /// <param name="continuationAction">Continuation action.</param> /// <param name="state">State.</param> /// <param name="cancellationToken">Cancellation token.</param> /// <param name="continuationOptions">Continuation options.</param> /// <param name="scheduler">Scheduler.</param> public Fiber ContinueWith(Action <Fiber, object> continuationAction, object state, CancellationToken cancellationToken, FiberContinuationOptions continuationOptions, FiberScheduler scheduler) { if (continuationAction == null) { throw new ArgumentNullException("continuationAction"); } if (scheduler == null) { throw new ArgumentNullException("scheduler"); } var fiber = new Fiber((obj) => continuationAction(this, obj), state, cancellationToken); fiber.antecedent = this; fiber.status = (int)FiberStatus.WaitingForActivation; var continuation = new FiberContinuation(fiber, continuationOptions, scheduler); if (IsCompleted) { continuation.Execute(); } else { // Lazy create queue if (continuations == null) { continuations = new Queue <FiberContinuation>(); } continuations.Enqueue(continuation); } return(fiber); }
/// <summary> /// Start executing a new fiber using the default scheduler on the thread. /// </summary> /// <returns> /// Returns a <see cref="Fiber"/> /// that can be yielded against to wait for the fiber to complete. /// </returns> /// <param name='action'> /// A non-blocking action to execute on the fiber. /// </param> /// <param name='state'> /// State to pass to the action. /// </param> /// <param name='scheduler'> /// A scheduler to execute the fiber on. /// </param> public Fiber StartNew(Action<object> action, object state, FiberScheduler scheduler) { return StartNew(action, state, cancellationToken, scheduler); }
/// <summary> /// Start executing a new fiber using the specified scheduler. /// </summary> /// <returns> /// Returns a <see cref="Fiber"/> /// that can be yielded against to wait for the fiber to complete. /// </returns> /// <param name='coroutine'> /// A couroutine to execute on the fiber. /// </param> /// <param name='scheduler'> /// A scheduler to execute the fiber on. /// </param> public Fiber StartNew(IEnumerator coroutine, FiberScheduler scheduler) { return StartNew(coroutine, cancellationToken, scheduler); }
/// <summary> /// Start executing the fiber using the specified scheduler. /// </summary> /// <remarks> /// This method is safe to call from any thread even if different /// than the scheduler execution thread. /// </remarks> /// <returns> /// Returns a <see cref="YieldUntilComplete"/> fiber instruction /// that can be yielded against to wait for the fiber to complete. /// </returns> /// <param name='scheduler'> /// The scheduler to start the fiber on. /// </param> public YieldUntilComplete Start(FiberScheduler scheduler) { // It would be unusual to attempt to start a Fiber more than once, // but to be safe and to support calling from any thread // use Interlocked with boxing on the enum. var originalState = (FiberState)Interlocked.CompareExchange(ref fiberState, (int)FiberState.Running, (int)FiberState.Unstarted); if(originalState != FiberState.Unstarted) throw new InvalidOperationException("A fiber cannot be started again once it has begun running or has completed."); this.scheduler = scheduler; ((IFiberScheduler)this.scheduler).QueueFiber(this); return new YieldUntilComplete(this); }
/// <summary> /// Returns a fiber that completes when any fiber finishes. /// </summary> /// <remarks> /// `Fiber.ResultAsObject` will be the `Fiber` that completed. /// </remarks> /// <returns>A fiber that completes when any fiber finishes.</returns> /// <param name="fibers">Fibers to wait for completion.</param> /// <param name="millisecondsTimeout">Milliseconds timeout.</param> /// <param name="cancellationToken">Cancellation token.</param> /// <param name="scheduler">Scheduler.</param> public static Fiber WhenAny(IEnumerable<Fiber> fibers, int millisecondsTimeout, CancellationToken cancellationToken, FiberScheduler scheduler) { return WhenAny(fibers.ToArray(), millisecondsTimeout, cancellationToken, scheduler); }
internal static void SetCurrentScheduler(FiberScheduler scheduler, bool internalInvoke) { // Only allow scheduler changes on a thread when a fiber is not executing. // Ignore this check if this is an internal invocation such as from // Fiber.Execute() which needs to switch schedulers on demand. if(!internalInvoke && Fiber.CurrentFiber != null) throw new InvalidOperationException("The current scheduler for the thread cannot be changed from inside an executing fiber."); // Skip work if nothing to change if(currentScheduler == scheduler) return; // Assign the scheduler currentScheduler = scheduler; // Update the synchronization context if it changed if(currentScheduler != null && SynchronizationContext.Current != currentScheduler.SynchronizationContext) SynchronizationContext.SetSynchronizationContext(currentScheduler.SynchronizationContext); else if(currentScheduler == null && SynchronizationContext.Current != null) SynchronizationContext.SetSynchronizationContext(null); }
/// <summary> /// Start executing a new fiber using the default scheduler on the thread. /// </summary> /// <returns> /// Returns a <see cref="Fiber"/> /// that can be yielded against to wait for the fiber to complete. /// </returns> /// <param name='action'> /// A non-blocking action to execute on the fiber. /// </param> /// <param name='scheduler'> /// A scheduler to execute the fiber on. /// </param> public Fiber StartNew(Action action, FiberScheduler scheduler) { return StartNew(action, cancellationToken, scheduler); }
/// <summary> /// Returns a fiber that waits on all fibers to complete. /// </summary> /// <remarks> /// `Fiber.ResultAsObject` will be `true` if all fibers complete /// successfully or `false` if cancelled or timeout. /// </remarks> /// <returns>A fiber that waits on all fibers to complete.</returns> /// <param name="fibers">Fibers to wait for completion.</param> /// <param name="millisecondsTimeout">Milliseconds timeout.</param> /// <param name="cancellationToken">Cancellation token.</param> /// <param name="scheduler">Scheduler.</param> public static Fiber WhenAll(IEnumerable <Fiber> fibers, int millisecondsTimeout, CancellationToken cancellationToken, FiberScheduler scheduler) { return(WhenAll(fibers.ToArray(), millisecondsTimeout, cancellationToken, scheduler)); }
/// <summary> /// Start executing a new fiber using the specified scheduler. /// </summary> /// <returns> /// Returns a <see cref="YieldUntilComplete"/> fiber instruction /// that can be yielded against to wait for the fiber to complete. /// </returns> /// <param name='coroutine'> /// A couroutine to execute on the fiber. /// </param> /// <param name='scheduler'> /// A scheduler to execute the fiber on. /// </param> public static YieldUntilComplete StartNew(IEnumerator coroutine, FiberScheduler scheduler) { return(new Fiber(coroutine).Start(scheduler)); }
/// <summary> /// Creates a continuation that executes asynchronously when the target fiber completes. /// </summary> /// <returns>A fiber that executes when the target fiber completes.</returns> /// <param name="continuationCoroutine">Continuation coroutine.</param> /// <param name="scheduler">Scheduler.</param> public Fiber ContinueWith(IEnumerator continuationCoroutine, FiberScheduler scheduler) { return(ContinueWith(continuationCoroutine, CancellationToken.None, FiberContinuationOptions.None, scheduler)); }
/// <summary> /// Creates a continuation that executes asynchronously when the target fiber completes. /// </summary> /// <returns>A fiber that executes when the target fiber completes.</returns> /// <param name="continuationAction">Continuation action.</param> /// <param name="state">State.</param> /// <param name="scheduler">Scheduler.</param> public Fiber ContinueWith(Action <Fiber, object> continuationAction, object state, FiberScheduler scheduler) { return(ContinueWith(continuationAction, state, CancellationToken.None, FiberContinuationOptions.None, scheduler)); }
/// <summary> /// Returns a fiber that waits on all tasks to complete. /// </summary> /// <remarks> /// `Fiber.ResultAsObject` will be `true` if all tasks complete /// successfully or `false` if cancelled or timeout. /// </remarks> /// <returns>A fiber that waits on all tasks to complete.</returns> /// <param name="tasks">Tasks to wait for completion.</param> /// <param name="millisecondsTimeout">Milliseconds timeout.</param> /// <param name="cancellationToken">Cancellation token.</param> /// <param name="scheduler">Scheduler.</param> public static Fiber WhenAll(IEnumerable<Task> tasks, int millisecondsTimeout, CancellationToken cancellationToken, FiberScheduler scheduler) { return WhenAll (tasks.ToArray (), millisecondsTimeout, cancellationToken, scheduler); }
/// <summary> /// Returns a fiber that waits on all tasks to complete. /// </summary> /// <remarks> /// `Fiber.ResultAsObject` will be `true` if all tasks complete /// successfully or `false` if cancelled or timeout. /// </remarks> /// <returns>A fiber that waits on all tasks to complete.</returns> /// <param name="tasks">Tasks to wait for completion.</param> /// <param name="millisecondsTimeout">Milliseconds timeout.</param> /// <param name="cancellationToken">Cancellation token.</param> /// <param name="scheduler">Scheduler.</param> public static Fiber WhenAll(Task [] tasks, int millisecondsTimeout, CancellationToken cancellationToken, FiberScheduler scheduler) { if (tasks == null) throw new ArgumentNullException ("tasks"); foreach (var fiber in tasks) { if (fiber == null) throw new ArgumentException ("tasks", "the tasks argument contains a null element"); } return Fiber.Factory.StartNew (WhenAllTasksCoroutine (tasks, millisecondsTimeout, cancellationToken), cancellationToken, scheduler); }
/// <summary> /// Initializes a new instance of the <see cref="SpicyPixel.Threading.FiberSchedulerSynchronizationContext"/> class. /// </summary> /// <param name='scheduler'> /// The scheduler to send or post callbacks to. /// </param> public FiberSchedulerSynchronizationContext(FiberScheduler scheduler) { this.scheduler = scheduler; }
/// <summary> /// Initializes a new instance of the <see cref="SpicyPixel.Threading.Fiber"/> class. /// </summary> /// <param name='func'> /// A non-blocking function that returns a <see cref="Fiber"/> when complete. /// </param> /// <param name='scheduler'> /// A scheduler to execute the fiber on. /// </param> public Fiber StartNew(Func<FiberInstruction> func, FiberScheduler scheduler) { return StartNew(func, cancellationToken, scheduler); }
/// <summary> /// Start executing a new fiber using the default scheduler on the thread. /// </summary> /// <returns> /// Returns a <see cref="YieldUntilComplete"/> fiber instruction /// that can be yielded against to wait for the fiber to complete. /// </returns> /// <param name='action'> /// A non-blocking action to execute on the fiber. /// </param> /// <param name='scheduler'> /// A scheduler to execute the fiber on. /// </param> /// <param name='state'> /// State to pass to the action. /// </param> public static YieldUntilComplete StartNew(Action <object> action, object state, FiberScheduler scheduler) { return(new Fiber(action, state).Start(scheduler)); }
/// <summary> /// Start executing a new fiber using the default scheduler on the thread. /// </summary> /// <returns> /// Returns a <see cref="Fiber"/> /// that can be yielded against to wait for the fiber to complete. /// </returns> /// <param name='action'> /// A non-blocking action to execute on the fiber. /// </param> /// <param name='state'> /// State to pass to the action. /// </param> /// <param name="cancellationToken">Cancellation token.</param> /// <param name='scheduler'> /// A scheduler to execute the fiber on. /// </param> public Fiber StartNew(Action<object> action, object state, CancellationToken cancellationToken, FiberScheduler scheduler) { var fiber = new Fiber(action, state, cancellationToken); fiber.Start(scheduler); return fiber; }
/// <summary> /// Initializes a new instance of the <see cref="SpicyPixel.Threading.Fiber"/> class. /// </summary> /// <param name='func'> /// A non-blocking function that returns a <see cref="Fiber"/> when complete. /// </param> /// <param name='state'> /// State to pass to the function. /// </param> /// <param name="cancellationToken">Cancellation token.</param> /// <param name='scheduler'> /// A scheduler to execute the fiber on. /// </param> public Fiber StartNew(Func<object, FiberInstruction> func, object state, CancellationToken cancellationToken, FiberScheduler scheduler) { var fiber = new Fiber(func, state, cancellationToken); fiber.Start(scheduler); return fiber; }
/// <summary> /// Returns a fiber that completes when any fiber finishes. /// </summary> /// <remarks> /// `Fiber.ResultAsObject` will be the `Fiber` that completed. /// </remarks> /// <returns>A fiber that completes when any fiber finishes.</returns> /// <param name="fibers">Fibers to wait for completion.</param> /// <param name="millisecondsTimeout">Milliseconds timeout.</param> /// <param name="cancellationToken">Cancellation token.</param> /// <param name="scheduler">Scheduler.</param> public static Fiber WhenAny(Fiber[] fibers, int millisecondsTimeout, CancellationToken cancellationToken, FiberScheduler scheduler) { if (fibers == null) { throw new ArgumentNullException("fibers"); } foreach (var fiber in fibers) { if (fiber == null) { throw new ArgumentException("fibers", "the fibers argument contains a null element"); } } return(Fiber.Factory.StartNew(WhenAnyFibersCoroutine(fibers, millisecondsTimeout, cancellationToken), cancellationToken, scheduler)); }
/// <summary> /// Start executing a new fiber using the specified scheduler. /// </summary> /// <returns> /// Returns a <see cref="YieldUntilComplete"/> fiber instruction /// that can be yielded against to wait for the fiber to complete. /// </returns> /// <param name='coroutine'> /// A couroutine to execute on the fiber. /// </param> /// <param name='scheduler'> /// A scheduler to execute the fiber on. /// </param> public static YieldUntilComplete StartNew(IEnumerator coroutine, FiberScheduler scheduler) { return new Fiber(coroutine).Start(scheduler); }
/// <summary> /// Start executing a new fiber using the specified scheduler. /// </summary> /// <returns> /// Returns a <see cref="Fiber"/> /// that can be yielded against to wait for the fiber to complete. /// </returns> /// <param name='coroutine'> /// A couroutine to execute on the fiber. /// </param> /// <param name="cancellationToken">Cancellation token.</param> /// <param name='scheduler'> /// A scheduler to execute the fiber on. /// </param> public Fiber StartNew(IEnumerator coroutine, CancellationToken cancellationToken, FiberScheduler scheduler) { var fiber = new Fiber(coroutine, cancellationToken); fiber.Start(scheduler); return(fiber); }
/// <summary> /// Start executing a new fiber using the default scheduler on the thread. /// </summary> /// <returns> /// Returns a <see cref="YieldUntilComplete"/> fiber instruction /// that can be yielded against to wait for the fiber to complete. /// </returns> /// <param name='action'> /// A non-blocking action to execute on the fiber. /// </param> /// <param name='scheduler'> /// A scheduler to execute the fiber on. /// </param> /// <param name='state'> /// State to pass to the action. /// </param> public static YieldUntilComplete StartNew(Action<object> action, object state, FiberScheduler scheduler) { return new Fiber(action, state).Start(scheduler); }
/// <summary> /// Initializes a new instance of the <see cref="SpicyPixel.Threading.Fiber"/> class. /// </summary> /// <param name='func'> /// A non-blocking function that returns a <see cref="Fiber"/> when complete. /// </param> /// <param name='state'> /// State to pass to the function. /// </param> /// <param name='scheduler'> /// A scheduler to execute the fiber on. /// </param> public Fiber StartNew(Func<object, FiberInstruction> func, object state, FiberScheduler scheduler) { return StartNew(func, state, cancellationToken, scheduler); }
/// <summary> /// Start executing a new fiber using the specified scheduler. /// </summary> /// <returns> /// Returns a <see cref="Fiber"/> /// that can be yielded against to wait for the fiber to complete. /// </returns> /// <param name='coroutine'> /// A couroutine to execute on the fiber. /// </param> /// <param name="cancellationToken">Cancellation token.</param> /// <param name='scheduler'> /// A scheduler to execute the fiber on. /// </param> public Fiber StartNew(IEnumerator coroutine, CancellationToken cancellationToken, FiberScheduler scheduler) { var fiber = new Fiber(coroutine, cancellationToken); fiber.Start(scheduler); return fiber; }
/// <summary> /// Start executing a new fiber using the default scheduler on the thread. /// </summary> /// <returns> /// Returns a <see cref="YieldUntilComplete"/> fiber instruction /// that can be yielded against to wait for the fiber to complete. /// </returns> /// <param name='action'> /// A non-blocking action to execute on the fiber. /// </param> /// <param name='scheduler'> /// A scheduler to execute the fiber on. /// </param> public static YieldUntilComplete StartNew(Action action, FiberScheduler scheduler) { return(new Fiber(action).Start(scheduler)); }
/// <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); } }
/// <summary> /// Initializes a new instance of the <see cref="SpicyPixel.Threading.Fiber"/> class. /// </summary> /// <param name='func'> /// A non-blocking function that returns a <see cref="FiberInstruction"/> when complete. /// </param> /// <param name='state'> /// State to pass to the function. /// </param> /// <param name='scheduler'> /// A scheduler to execute the fiber on. /// </param> public static YieldUntilComplete StartNew(Func <object, FiberInstruction> func, object state, FiberScheduler scheduler) { return(new Fiber(func, state).Start(scheduler)); }
public FiberContinuation(Fiber fiber, FiberContinuationOptions options, FiberScheduler scheduler) { this.fiber = fiber; this.options = options; this.scheduler = scheduler; }
/// <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; } }
/// <summary> /// Initializes a new instance of the <see cref="SpicyPixel.Threading.FiberFactory"/> class. /// </summary> /// <param name="scheduler">Scheduler.</param> public FiberFactory(FiberScheduler scheduler) : this(CancellationToken.None, FiberContinuationOptions.None, scheduler) { }
/// <summary> /// Start executing a new fiber using the default scheduler on the thread. /// </summary> /// <returns> /// Returns a <see cref="YieldUntilComplete"/> fiber instruction /// that can be yielded against to wait for the fiber to complete. /// </returns> /// <param name='action'> /// A non-blocking action to execute on the fiber. /// </param> /// <param name='scheduler'> /// A scheduler to execute the fiber on. /// </param> public static YieldUntilComplete StartNew(Action action, FiberScheduler scheduler) { return new Fiber(action).Start(scheduler); }
/// <summary> /// Start executing a new fiber using the default scheduler on the thread. /// </summary> /// <returns> /// Returns a <see cref="Fiber"/> /// that can be yielded against to wait for the fiber to complete. /// </returns> /// <param name='action'> /// A non-blocking action to execute on the fiber. /// </param> /// <param name='scheduler'> /// A scheduler to execute the fiber on. /// </param> public Fiber StartNew(Action action, FiberScheduler scheduler) { return(StartNew(action, cancellationToken, scheduler)); }
/// <summary> /// Initializes a new instance of the <see cref="SpicyPixel.Threading.Fiber"/> class. /// </summary> /// <param name='func'> /// A non-blocking function that returns a <see cref="FiberInstruction"/> when complete. /// </param> /// <param name='state'> /// State to pass to the function. /// </param> /// <param name='scheduler'> /// A scheduler to execute the fiber on. /// </param> public static YieldUntilComplete StartNew(Func<object, FiberInstruction> func, object state, FiberScheduler scheduler) { return new Fiber(func, state).Start(scheduler); }
/// <summary> /// Crates a Fiber that waits for a delay before completing. /// </summary> /// <param name="millisecondsDelay">Milliseconds to delay.</param> /// <param name="cancellationToken">Cancellation token.</param> /// <param name="scheduler">Scheduler.</param> public static Fiber Delay(int millisecondsDelay, CancellationToken cancellationToken, FiberScheduler scheduler) { if (millisecondsDelay < -1) { throw new ArgumentOutOfRangeException("millisecondsDelay"); } return(Fiber.Factory.StartNew(DelayCoroutine(millisecondsDelay, cancellationToken), scheduler)); }
/// <summary> /// Initializes a new instance of the <see cref="SpicyPixel.Threading.FiberFactory"/> class. /// </summary> /// <param name="scheduler">Scheduler.</param> public FiberFactory(FiberScheduler scheduler) : this(CancellationToken.None, FiberContinuationOptions.None, scheduler) { }
/// <summary> /// Creates a continuation that executes asynchronously when the target fiber completes. /// </summary> /// <returns>A fiber that executes when the target fiber completes.</returns> /// <param name="continuationAction">Continuation action.</param> /// <param name="state">State.</param> /// <param name="scheduler">Scheduler.</param> public Fiber ContinueWith(Action<Fiber, object> continuationAction, object state, FiberScheduler scheduler) { return ContinueWith(continuationAction, state, CancellationToken.None, FiberContinuationOptions.None, scheduler); }
public FiberContinuation(Fiber fiber, FiberContinuationOptions options, FiberScheduler scheduler) { this.fiber = fiber; this.options = options; this.scheduler = scheduler; }
/// <summary> /// Start executing a new fiber using the default scheduler on the thread. /// </summary> /// <returns> /// Returns a <see cref="Fiber"/> /// that can be yielded against to wait for the fiber to complete. /// </returns> /// <param name='action'> /// A non-blocking action to execute on the fiber. /// </param> /// <param name="cancellationToken">Cancellation token.</param> /// <param name='scheduler'> /// A scheduler to execute the fiber on. /// </param> public Fiber StartNew(Action action, CancellationToken cancellationToken, FiberScheduler scheduler) { var fiber = new Fiber(action, cancellationToken); fiber.Start(scheduler); return fiber; }
/// <summary> /// Creates a continuation that executes asynchronously when the target fiber completes. /// </summary> /// <returns>A fiber that executes when the target fiber completes.</returns> /// <param name="continuationAction">Continuation action.</param> /// <param name="cancellationToken">Cancellation token.</param> /// <param name="continuationOptions">Continuation options.</param> /// <param name="scheduler">Scheduler.</param> public Fiber ContinueWith(Action<Fiber> continuationAction, CancellationToken cancellationToken, FiberContinuationOptions continuationOptions, FiberScheduler scheduler) { return ContinueWith((fiber, state) => continuationAction(fiber), null, cancellationToken, continuationOptions, scheduler); }
/// <summary> /// Start executing a new fiber using the default scheduler on the thread. /// </summary> /// <returns> /// Returns a <see cref="Fiber"/> /// that can be yielded against to wait for the fiber to complete. /// </returns> /// <param name='action'> /// A non-blocking action to execute on the fiber. /// </param> /// <param name='state'> /// State to pass to the action. /// </param> /// <param name='scheduler'> /// A scheduler to execute the fiber on. /// </param> public Fiber StartNew(Action <object> action, object state, FiberScheduler scheduler) { return(StartNew(action, state, cancellationToken, scheduler)); }
/// <summary> /// Creates a continuation that executes asynchronously when the target fiber completes. /// </summary> /// <returns>A fiber that executes when the target fiber completes.</returns> /// <param name="continuationCoroutine">Continuation coroutine.</param> /// <param name="scheduler">Scheduler.</param> public Fiber ContinueWith(IEnumerator continuationCoroutine, FiberScheduler scheduler) { return ContinueWith(continuationCoroutine, CancellationToken.None, FiberContinuationOptions.None, scheduler); }
/// <summary> /// Start executing a new fiber using the default scheduler on the thread. /// </summary> /// <returns> /// Returns a <see cref="Fiber"/> /// that can be yielded against to wait for the fiber to complete. /// </returns> /// <param name='action'> /// A non-blocking action to execute on the fiber. /// </param> /// <param name='state'> /// State to pass to the action. /// </param> /// <param name="cancellationToken">Cancellation token.</param> /// <param name='scheduler'> /// A scheduler to execute the fiber on. /// </param> public Fiber StartNew(Action <object> action, object state, CancellationToken cancellationToken, FiberScheduler scheduler) { var fiber = new Fiber(action, state, cancellationToken); fiber.Start(scheduler); return(fiber); }
/// <summary> /// Initializes a new instance of the <see cref="SpicyPixel.Threading.FiberSchedulerSynchronizationContext"/> class. /// </summary> /// <param name='scheduler'> /// The scheduler to send or post callbacks to. /// </param> public FiberSchedulerSynchronizationContext(FiberScheduler scheduler) { this.scheduler = scheduler; }
/// <summary> /// Initializes a new instance of the <see cref="SpicyPixel.Threading.Fiber"/> class. /// </summary> /// <param name='func'> /// A non-blocking function that returns a <see cref="Fiber"/> when complete. /// </param> /// <param name='scheduler'> /// A scheduler to execute the fiber on. /// </param> public Fiber StartNew(Func <FiberInstruction> func, FiberScheduler scheduler) { return(StartNew(func, cancellationToken, scheduler)); }
/// <summary> /// Start executing the fiber using the specified scheduler. /// </summary> /// <remarks> /// This method is safe to call from any thread even if different /// than the scheduler execution thread. /// </remarks> /// <param name='scheduler'> /// The scheduler to start the fiber on. /// </param> public void Start(FiberScheduler scheduler) { // It would be unusual to attempt to start a Fiber more than once, // but to be safe and to support calling from any thread // use Interlocked with boxing on the enum. var originalState = (FiberStatus)Interlocked.CompareExchange (ref status, (int)FiberStatus.WaitingToRun, (int)FiberStatus.Created); if (originalState != FiberStatus.Created) { originalState = (FiberStatus)Interlocked.CompareExchange (ref status, (int)FiberStatus.WaitingToRun, (int)FiberStatus.WaitingForActivation); if (originalState != FiberStatus.WaitingForActivation) { throw new InvalidOperationException ("A fiber cannot be started again once it has begun running or has completed."); } } this.scheduler = scheduler; ((IFiberScheduler)this.scheduler).QueueFiber (this); }
/// <summary> /// Initializes a new instance of the <see cref="SpicyPixel.Threading.Fiber"/> class. /// </summary> /// <param name='func'> /// A non-blocking function that returns a <see cref="Fiber"/> when complete. /// </param> /// <param name='state'> /// State to pass to the function. /// </param> /// <param name='scheduler'> /// A scheduler to execute the fiber on. /// </param> public Fiber StartNew(Func <object, FiberInstruction> func, object state, FiberScheduler scheduler) { return(StartNew(func, state, cancellationToken, scheduler)); }
/// <summary> /// Returns a fiber that completes when any task finishes. /// </summary> /// <remarks> /// `Fiber.ResultAsObject` will be the `Task` that completed. /// </remarks> /// <returns>A fiber that completes when any task finishes.</returns> /// <param name="tasks">Tasks to wait for completion.</param> /// <param name="millisecondsTimeout">Milliseconds timeout.</param> /// <param name="cancellationToken">Cancellation token.</param> /// <param name="scheduler">Scheduler.</param> public static Fiber WhenAny(IEnumerable <Task> tasks, int millisecondsTimeout, CancellationToken cancellationToken, FiberScheduler scheduler) { return(WhenAny(tasks.ToArray(), millisecondsTimeout, cancellationToken, scheduler)); }
/// <summary> /// Initializes a new instance of the <see cref="SpicyPixel.Threading.Fiber"/> class. /// </summary> /// <param name='func'> /// A non-blocking function that returns a <see cref="Fiber"/> when complete. /// </param> /// <param name='state'> /// State to pass to the function. /// </param> /// <param name="cancellationToken">Cancellation token.</param> /// <param name='scheduler'> /// A scheduler to execute the fiber on. /// </param> public Fiber StartNew(Func <object, FiberInstruction> func, object state, CancellationToken cancellationToken, FiberScheduler scheduler) { var fiber = new Fiber(func, state, cancellationToken); fiber.Start(scheduler); return(fiber); }
/// <summary> /// Initializes the task factory during Awake(). /// </summary> /// <remarks> /// The task factory cannot be initialized in the constructor /// because it must be initialized from the coroutine /// execution thread which the constructor does not guarantee. /// </remarks> protected virtual void Awake() { _taskFactory = this.CreateTaskFactory(); _taskScheduler = _taskFactory.Scheduler; _fiberScheduler = ((FiberTaskScheduler)_taskScheduler).FiberScheduler; _fiberFactory = new FiberFactory(_fiberScheduler); }
/// <summary> /// Start executing a new fiber using the specified scheduler. /// </summary> /// <returns> /// Returns a <see cref="Fiber"/> /// that can be yielded against to wait for the fiber to complete. /// </returns> /// <param name='coroutine'> /// A couroutine to execute on the fiber. /// </param> /// <param name='scheduler'> /// A scheduler to execute the fiber on. /// </param> public Fiber StartNew(IEnumerator coroutine, FiberScheduler scheduler) { return(StartNew(coroutine, cancellationToken, scheduler)); }