/// <summary> /// Enqueues absolute time scheduled work in the timer queue or the short term work list. /// </summary> /// <param name="state">State to pass to the action.</param> /// <param name="dueTime">Absolute time to run the work on. The timer queue is responsible to execute the work close to the specified time, also accounting for system clock changes.</param> /// <param name="action">Action to run, potentially recursing into the scheduler.</param> /// <returns>Disposable object to prevent the work from running.</returns> private IDisposable Enqueue <TState>(TState state, DateTimeOffset dueTime, Func <IScheduler, TState, IDisposable> action) { // // Work that's due in the past is sent to the underlying scheduler through the Schedule // overload for execution at TimeSpan.Zero. We don't go to the overload for immediate // scheduling in order to: // // - Preserve the time-based nature of the call as surfaced to the underlying scheduler, // as it may use different queuing strategies. // // - Optimize for the default behavior of LocalScheduler where a virtual call to Schedule // for immediate execution calls into the abstract Schedule method with TimeSpan.Zero. // var due = Scheduler.Normalize(dueTime - Now); if (due == TimeSpan.Zero) { return(Schedule <TState>(state, TimeSpan.Zero, action)); } // // We're going down the path of queueing up work or scheduling it, so we need to make // sure we can get system clock change notifications. If not, the call below is expected // to throw NotSupportedException. WorkItem.Invoke decreases the ref count again to allow // the system clock monitor to stop if there's no work left. Notice work items always // reach an execution stage since we don't dequeue items but merely mark them as cancelled // through WorkItem.Dispose. Double execution is also prevented, so the ref count should // correctly balance out. // SystemClock.AddRef(); var workItem = new WorkItem <TState>(this, state, dueTime, action); if (due <= SHORTTERM) { ScheduleShortTermWork(workItem); } else { ScheduleLongTermWork(workItem); } return(workItem); }