/// <summary>
 /// Dispatches an asynchronous message to a synchronization context (the <see cref="FiberScheduler"/>).
 /// </summary>
 /// <remarks>
 /// The scheduler may choose to inline the callback if the Post is
 /// executed from the scheduler thread.
 /// </remarks>
 /// <param name='d'>
 /// Callback to invoke
 /// </param>
 /// <param name='state'>
 /// State to pass
 /// </param>
 public override void Post(SendOrPostCallback d, object state)
 {
     // The scheduler may choose to inline this if the Post
     // is executed from the scheduler thread.
     Fiber.StartNew(() => {
         d(state);
     }, scheduler);
 }
        /// <summary>
        /// Dispatches an synchronous message to a synchronization context (the <see cref="FiberScheduler"/>).
        /// </summary>
        /// <remarks>
        /// The callback is always inlined if Send is executed from the
        /// scheduler thread regardless of any scheduler specific inline settings.
        /// Because inlining always occurs when on the scheduler thread, the
        /// caller must manage stack depth.
        /// </remarks>
        /// <param name='d'>
        /// Callback to invoke
        /// </param>
        /// <param name='state'>
        /// State to pass
        /// </param>
        public override void Send(SendOrPostCallback d, object state)
        {
            // Force inlining if the threads match.
            if (scheduler.SchedulerThread == Thread.CurrentThread)
            {
                d(state);
                return;
            }

            // FIXME: This could block indefinitely if the scheduler goes down
            // before executing the task. Need another wait handle here or
            // better approach. Maybe add a WaitHandle to the fiber itself or
            // add Join().

            // The threads don't match, so queue the action
            // and wait for it to complete.
            ManualResetEvent wait = new ManualResetEvent(false);

            Fiber.StartNew(() => {
                d(state);
                wait.Set();
            }, scheduler);
            wait.WaitOne();
        }
        /// <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();
            }
        }