/// <summary> /// Schedule the events to process. /// </summary> /// <remarks> /// A major assumption used by this implementation is that, when used in /// conjunction with engine failovers, if a failover occurs at the same /// scheduler tick as one of the events in the timeline, the failover must /// always be scheduled to occur before the timeline event is scheduled /// (note the `dueTime >= _scheduler.Clock` check below). If the assumption /// does not hold true, then the timeline event will occur twice. /// </remarks> private void DoScheduling() { // For cold-scheduled events, we should always start from the beginning; // for hot-scheduled events, we should start from the last event processed. var startIndex = Params._useRelativeScheduling ? 0 : Params._eventIndex; for (var i = SchedulingStartIndex; i < _events.Count; ++i) { // For cold-scheduled events, we should set the due time based on the subscribe time; // For hot-scheduled events, we should set the due time to the absolute time given. var dueTime = DueTime(_events[i].Time); if (dueTime >= _scheduler.Clock) { var @event = _events[i].Value; _scheduler.ScheduleAbsolute(dueTime, () => { if (!IsDisposed) { ProcessEvent(@event); } }); } else { // Keep the event index up to date in case resubscribes occur Params._eventIndex++; } } }
public static void ScheduleAbsolute(this ITestScheduler scheduler, long dueTime, Func <Task> asyncAction) { if (scheduler == null) { throw new ArgumentNullException(nameof(scheduler)); } if (asyncAction == null) { throw new ArgumentNullException(nameof(asyncAction)); } scheduler.ScheduleAbsolute(dueTime, new RemoteSchedulerTask(new AsyncClientAction(asyncAction))); }