/// <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> /// 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="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> /// 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); }
public void Execute() { // In case ran, faulted, canceled externally if (fiber.IsCompleted) { return; } // Cancel the fiber if criteria is not met if (!ContinuationStateCheck(options)) { fiber.CancelContinuation(); return; } fiber.Start(scheduler); }
/// <summary> /// Run the blocking scheduler loop and perform the specified number of updates per second. /// </summary> /// <remarks> /// Not all schedulers support a blocking run loop that can be invoked by the caller. /// The system scheduler is designed so that a custom run loop could be implemented /// by a derived type. Everything used to execute Run() is available to a derived scheduler. /// </remarks> /// <param name='fiber'> /// The optional fiber to start execution from. If this is <c>null</c>, the loop /// will continue to execute until cancelled. Otherwise, the loop will terminate /// when the fiber terminates. /// </param> /// <param name='updatesPerSecond'> /// Updates to all fibers per second. A value of <c>0</c> (the default) will execute fibers /// any time they are ready to do work instead of waiting to execute on a specific frequency. /// </param> /// <param name='token'> /// A cancellation token that can be used to stop execution. /// </param> public override void Run(Fiber fiber, CancellationToken token, float updatesPerSecond) { long frequencyTicks = (long)(updatesPerSecond * (float)TimeSpan.TicksPerSecond); // min time between updates (duration) long startTicks = 0; // start of update time (marker) long endTicks = 0; // end of update time (marker) long sleepTicks; // time to sleep (duration) long wakeTicks; // ticks before wake (duration) int sleepMilliseconds; // ms to sleep (duration) int wakeMilliseconds; // ms before wake (duration) float wakeMarkerInSeconds; // time of wake in seconds (marker) var mainFiberCompleteCancelSource = new CancellationTokenSource(); if(isDisposed) throw new ObjectDisposedException(GetType().FullName); // Run is not re-entrant, make sure we're not running if(!runWaitHandle.WaitOne(0)) throw new InvalidOperationException("Run is already executing and is not re-entrant"); // Verify arguments if(updatesPerSecond < 0f) throw new ArgumentOutOfRangeException("updatesPerSecond", "The updatesPerSecond must be >= 0"); // Get a base time for better precision long baseTicks = DateTime.Now.Ticks; // Build wait list to terminate execution var waitHandleList = new List<WaitHandle>(4); waitHandleList.Add(schedulerEventWaitHandle); waitHandleList.Add(disposeWaitHandle); if(token.CanBeCanceled) waitHandleList.Add(token.WaitHandle); try { if(fiber != null) { // Add the main fiber to the wait list so when it completes // the wait handle falls through. waitHandleList.Add(mainFiberCompleteCancelSource.Token.WaitHandle); // Start the main fiber if it isn't running yet YieldUntilComplete waitOnFiber; if(fiber.FiberState == FiberState.Unstarted) waitOnFiber = fiber.Start(this); else waitOnFiber = new YieldUntilComplete(fiber); // Start another fiber that waits on the main fiber to complete. // When it does, it raises a cancellation. Fiber.StartNew(CancelWhenComplete(waitOnFiber, mainFiberCompleteCancelSource), this); } WaitHandle[] waitHandles = waitHandleList.ToArray(); waitHandleList.Remove(schedulerEventWaitHandle); WaitHandle[] sleepWaitHandles = waitHandleList.ToArray(); runWaitHandle.Reset(); while(true) { // Stop executing if cancelled if((token.CanBeCanceled && token.IsCancellationRequested) || mainFiberCompleteCancelSource.IsCancellationRequested || disposeWaitHandle.WaitOne(0)) return; // Snap current time startTicks = DateTime.Now.Ticks; // Update using this time marker (and convert ticks to s) Update((float)((double)(startTicks - baseTicks) / (double)TimeSpan.TicksPerSecond)); // Only sleep to next frequency cycle if one was specified if(updatesPerSecond > 0f) { // Snap end time endTicks = DateTime.Now.Ticks; // Sleep at least until next update sleepTicks = frequencyTicks - (endTicks - startTicks); if(sleepTicks > 0) { sleepMilliseconds = (int)(sleepTicks / TimeSpan.TicksPerMillisecond); WaitHandle.WaitAny(sleepWaitHandles, sleepMilliseconds); // Stop executing if cancelled if((token.CanBeCanceled && token.IsCancellationRequested) || mainFiberCompleteCancelSource.IsCancellationRequested || disposeWaitHandle.WaitOne(0)) return; } } // Now keep sleeping until it's time to update while(ExecutingFiberCount == 0) { // Assume we wait forever (e.g. until a signal) wakeMilliseconds = -1; // If there are sleeping fibers, then set a wake time if(GetNextFiberWakeTime(out wakeMarkerInSeconds)) { wakeTicks = baseTicks; wakeTicks += (long)((double)wakeMarkerInSeconds * (double)TimeSpan.TicksPerSecond); wakeTicks -= DateTime.Now.Ticks; // If there was a waiting fiber and it's already past time to awake then stop waiting if(wakeTicks <= 0) break; wakeMilliseconds = (int)(wakeTicks / TimeSpan.TicksPerMillisecond); } // There was no waiting fiber and we will wait for another signal, // or there was a waiting fiber and we wait until that time. WaitHandle.WaitAny(waitHandles, wakeMilliseconds); // Stop executing if cancelled if((token.CanBeCanceled && token.IsCancellationRequested) || mainFiberCompleteCancelSource.IsCancellationRequested || disposeWaitHandle.WaitOne(0)) return; } } } finally { // Clear queues Fiber deqeueFiber; while(executingFibers.TryDequeue(out deqeueFiber)); Tuple<Fiber, float> dequeueSleepingFiber; while(sleepingFibers.TryDequeue(out dequeueSleepingFiber)); // Reset time currentTime = 0f; // Set for dispose runWaitHandle.Set(); } }
/// <summary> /// Run the blocking scheduler loop and perform the specified number of updates per second. /// </summary> /// <remarks> /// Not all schedulers support a blocking run loop that can be invoked by the caller. /// The system scheduler is designed so that a custom run loop could be implemented /// by a derived type. Everything used to execute Run() is available to a derived scheduler. /// </remarks> /// <param name='fiber'> /// The optional fiber to start execution from. If this is <c>null</c>, the loop /// will continue to execute until cancelled. Otherwise, the loop will terminate /// when the fiber terminates. /// </param> /// <param name='updatesPerSecond'> /// Updates to all fibers per second. A value of <c>0</c> (the default) will execute fibers /// any time they are ready to do work instead of waiting to execute on a specific frequency. /// </param> /// <param name='token'> /// A cancellation token that can be used to stop execution. /// </param> public override void Run(Fiber fiber, CancellationToken token, float updatesPerSecond) { long frequencyTicks = (long)(updatesPerSecond * (float)TimeSpan.TicksPerSecond); // min time between updates (duration) long startTicks = 0; // start of update time (marker) long endTicks = 0; // end of update time (marker) long sleepTicks; // time to sleep (duration) long wakeTicks; // ticks before wake (duration) int sleepMilliseconds; // ms to sleep (duration) int wakeMilliseconds; // ms before wake (duration) float wakeMarkerInSeconds; // time of wake in seconds (marker) var mainFiberCompleteCancelSource = new CancellationTokenSource(); if (isDisposed) { throw new ObjectDisposedException(GetType().FullName); } // Run is not re-entrant, make sure we're not running if (!runWaitHandle.WaitOne(0)) { throw new InvalidOperationException("Run is already executing and is not re-entrant"); } // Verify arguments if (updatesPerSecond < 0f) { throw new ArgumentOutOfRangeException("updatesPerSecond", "The updatesPerSecond must be >= 0"); } // Get a base time for better precision long baseTicks = DateTime.Now.Ticks; // Build wait list to terminate execution var waitHandleList = new List <WaitHandle>(4); waitHandleList.Add(schedulerEventWaitHandle); waitHandleList.Add(disposeWaitHandle); if (token.CanBeCanceled) { waitHandleList.Add(token.WaitHandle); } try { if (fiber != null) { // Add the main fiber to the wait list so when it completes // the wait handle falls through. waitHandleList.Add(mainFiberCompleteCancelSource.Token.WaitHandle); // Start the main fiber if it isn't running yet YieldUntilComplete waitOnFiber; if (fiber.FiberState == FiberState.Unstarted) { waitOnFiber = fiber.Start(this); } else { waitOnFiber = new YieldUntilComplete(fiber); } // Start another fiber that waits on the main fiber to complete. // When it does, it raises a cancellation. Fiber.StartNew(CancelWhenComplete(waitOnFiber, mainFiberCompleteCancelSource), this); } WaitHandle[] waitHandles = waitHandleList.ToArray(); waitHandleList.Remove(schedulerEventWaitHandle); WaitHandle[] sleepWaitHandles = waitHandleList.ToArray(); runWaitHandle.Reset(); while (true) { // Stop executing if cancelled if ((token.CanBeCanceled && token.IsCancellationRequested) || mainFiberCompleteCancelSource.IsCancellationRequested || disposeWaitHandle.WaitOne(0)) { return; } // Snap current time startTicks = DateTime.Now.Ticks; // Update using this time marker (and convert ticks to s) Update((float)((double)(startTicks - baseTicks) / (double)TimeSpan.TicksPerSecond)); // Only sleep to next frequency cycle if one was specified if (updatesPerSecond > 0f) { // Snap end time endTicks = DateTime.Now.Ticks; // Sleep at least until next update sleepTicks = frequencyTicks - (endTicks - startTicks); if (sleepTicks > 0) { sleepMilliseconds = (int)(sleepTicks / TimeSpan.TicksPerMillisecond); WaitHandle.WaitAny(sleepWaitHandles, sleepMilliseconds); // Stop executing if cancelled if ((token.CanBeCanceled && token.IsCancellationRequested) || mainFiberCompleteCancelSource.IsCancellationRequested || disposeWaitHandle.WaitOne(0)) { return; } } } // Now keep sleeping until it's time to update while (ExecutingFiberCount == 0) { // Assume we wait forever (e.g. until a signal) wakeMilliseconds = -1; // If there are sleeping fibers, then set a wake time if (GetNextFiberWakeTime(out wakeMarkerInSeconds)) { wakeTicks = baseTicks; wakeTicks += (long)((double)wakeMarkerInSeconds * (double)TimeSpan.TicksPerSecond); wakeTicks -= DateTime.Now.Ticks; // If there was a waiting fiber and it's already past time to awake then stop waiting if (wakeTicks <= 0) { break; } wakeMilliseconds = (int)(wakeTicks / TimeSpan.TicksPerMillisecond); } // There was no waiting fiber and we will wait for another signal, // or there was a waiting fiber and we wait until that time. WaitHandle.WaitAny(waitHandles, wakeMilliseconds); // Stop executing if cancelled if ((token.CanBeCanceled && token.IsCancellationRequested) || mainFiberCompleteCancelSource.IsCancellationRequested || disposeWaitHandle.WaitOne(0)) { return; } } } } finally { // Clear queues Fiber deqeueFiber; while (executingFibers.TryDequeue(out deqeueFiber)) { ; } Tuple <Fiber, float> dequeueSleepingFiber; while (sleepingFibers.TryDequeue(out dequeueSleepingFiber)) { ; } // Reset time currentTime = 0f; // Set for dispose runWaitHandle.Set(); } }
public void TestYieldToFiber() { using (var backgroundFiberScheduler = SystemFiberScheduler.StartNew()) { backgroundFiberScheduler.AllowInlining = true; var f1 = new Fiber(IncrementerCoroutine1()); f1.Start(backgroundFiberScheduler); backgroundFiberScheduler.SchedulerThread.Join(2000); } Assert.AreEqual(yieldToFiberCounter2, yieldToFiberCounter1 * 2); }
/// <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> /// 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> /// 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> /// 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; }