/// <summary> /// Checks whether the schedule is currently past due. /// </summary> /// <remarks> /// On startup, all schedules are checked to see if they are past due. Any /// timers that are past due will be executed immediately by default. Subclasses can /// change this behavior by inspecting the current time and schedule to determine /// whether it should be considered past due. /// </remarks> /// <param name="timerName">The name of the timer to check.</param> /// <param name="now">The time to check.</param> /// <param name="schedule">The <see cref="TimerSchedule"/></param> /// <param name="lastStatus">The last recorded status, or null if the status has never been recorded.</param> /// <returns>A non-zero <see cref="TimeSpan"/> if the schedule is past due, otherwise <see cref="TimeSpan.Zero"/>.</returns> public virtual async Task <TimeSpan> CheckPastDueAsync(string timerName, DateTime now, TimerSchedule schedule, ScheduleStatus lastStatus) { DateTime recordedNextOccurrence; if (lastStatus == null) { // If we've never recorded a status for this timer, write an initial // status entry. This ensures that for a new timer, we've captured a // status log for the next occurrence even though no occurrence has happened yet // (ensuring we don't miss an occurrence) DateTime nextOccurrence = schedule.GetNextOccurrence(now); lastStatus = new ScheduleStatus { Last = default(DateTime), Next = nextOccurrence }; await UpdateStatusAsync(timerName, lastStatus); recordedNextOccurrence = nextOccurrence; } else { // ensure that the schedule hasn't been updated since the last // time we checked, and if it has, update the status DateTime expectedNextOccurrence; if (lastStatus.Last == default(DateTime)) { // there have been no executions of the function yet, so compute // from now expectedNextOccurrence = schedule.GetNextOccurrence(now); } else { // compute the next occurrence from the last expectedNextOccurrence = schedule.GetNextOccurrence(lastStatus.Last); } if (lastStatus.Next != expectedNextOccurrence) { lastStatus.Next = expectedNextOccurrence; await UpdateStatusAsync(timerName, lastStatus); } recordedNextOccurrence = lastStatus.Next; } if (now > recordedNextOccurrence) { // if now is after the last next occurrence we recorded, we know we've missed // at least one schedule instance and we are past due return(now - recordedNextOccurrence); } else { // not past due return(TimeSpan.Zero); } }
/// <summary> /// Checks whether the schedule is currently past due. /// </summary> /// <remarks> /// On startup, all schedules are checked to see if they are past due. Any /// timers that are past due will be executed immediately by default. Subclasses can /// change this behavior by inspecting the current time and schedule to determine /// whether it should be considered past due. /// </remarks> /// <param name="timerName">The name of the timer to check.</param> /// <param name="now">The time to check.</param> /// <param name="schedule">The <see cref="TimerSchedule"/></param> /// <param name="lastStatus">The last recorded status, or null if the status has never been recorded.</param> /// <returns>A non-zero <see cref="TimeSpan"/> if the schedule is past due, otherwise <see cref="TimeSpan.Zero"/>.</returns> public virtual async Task<TimeSpan> CheckPastDueAsync(string timerName, DateTime now, TimerSchedule schedule, ScheduleStatus lastStatus) { DateTime recordedNextOccurrence; if (lastStatus == null) { // If we've never recorded a status for this timer, write an initial // status entry. This ensures that for a new timer, we've captured a // status log for the next occurrence even though no occurrence has happened yet // (ensuring we don't miss an occurrence) DateTime nextOccurrence = schedule.GetNextOccurrence(now); lastStatus = new ScheduleStatus { Last = default(DateTime), Next = nextOccurrence }; await UpdateStatusAsync(timerName, lastStatus); recordedNextOccurrence = nextOccurrence; } else { // ensure that the schedule hasn't been updated since the last // time we checked, and if it has, update the status DateTime expectedNextOccurrence; if (lastStatus.Last == default(DateTime)) { // there have been no executions of the function yet, so compute // from now expectedNextOccurrence = schedule.GetNextOccurrence(now); } else { // compute the next occurrence from the last expectedNextOccurrence = schedule.GetNextOccurrence(lastStatus.Last); } if (lastStatus.Next != expectedNextOccurrence) { lastStatus.Next = expectedNextOccurrence; await UpdateStatusAsync(timerName, lastStatus); } recordedNextOccurrence = lastStatus.Next; } if (now > recordedNextOccurrence) { // if now is after the last next occurrence we recorded, we know we've missed // at least one schedule instance and we are past due return now - recordedNextOccurrence; } else { // not past due return TimeSpan.Zero; } }
/// <summary> /// Returns the <see cref="TimeSpan"/> duration that the specified timer is past due. /// </summary> /// <param name="timerName">The name of the timer.</param> /// <param name="now">The current time.</param> /// <param name="schedule">The <see cref="TimerSchedule"/>.</param> /// <returns>The duration the timer is past due.</returns> protected async Task<TimeSpan> GetPastDueDuration(string timerName, DateTime now, TimerSchedule schedule) { StatusEntry status = GetStatus(timerName); DateTime recordedNextOccurrence; if (status == null) { // If we've never recorded a status for this timer, write an initial // status entry. This ensures that for a new timer, we've captured a // status log for the next occurrence even though no occurrence has happened yet // (ensuring we don't miss an occurrence) DateTime nextOccurrence = schedule.GetNextOccurrence(now); await UpdateAsync(timerName, default(DateTime), nextOccurrence); recordedNextOccurrence = nextOccurrence; } else { // ensure that the schedule hasn't been updated since the last // time we checked, and if it has, update the status file DateTime expectedNextOccurrence = schedule.GetNextOccurrence(status.Last); if (status.Next != expectedNextOccurrence) { await UpdateAsync(timerName, status.Last, expectedNextOccurrence); } recordedNextOccurrence = status.Next; } if (now > recordedNextOccurrence) { // if now is after the last next occurrence we recorded, we know we've missed // at least one schedule instance and we are past due return now - recordedNextOccurrence; } else { // not past due return TimeSpan.Zero; } }
/// <summary> /// Checks whether the schedule is currently past due. /// </summary> /// <remarks> /// On startup, all schedules are checked to see if they are past due. Any /// timers that are past due will be executed immediately by default. Subclasses can /// change this behavior by inspecting the current time and schedule to determine /// whether it should be considered past due. /// </remarks> /// <param name="timerName">The name of the timer to check.</param> /// <param name="now">The time to check.</param> /// <param name="schedule">The <see cref="TimerSchedule"/>.</param> /// <param name="lastStatus">The last recorded status, or null if the status has never been recorded.</param> /// <returns>A non-zero <see cref="TimeSpan"/> if the schedule is past due, otherwise <see cref="TimeSpan.Zero"/>.</returns> public virtual async Task <TimeSpan> CheckPastDueAsync(string timerName, DateTime now, TimerSchedule schedule, ScheduleStatus lastStatus) { DateTime recordedNextOccurrence; if (lastStatus == null) { // If we've never recorded a status for this timer, write an initial // status entry. This ensures that for a new timer, we've captured a // status log for the next occurrence even though no occurrence has happened yet // (ensuring we don't miss an occurrence) DateTime nextOccurrence = schedule.GetNextOccurrence(now); lastStatus = new ScheduleStatus { Last = default(DateTime), Next = nextOccurrence, LastUpdated = now }; await UpdateStatusAsync(timerName, lastStatus); recordedNextOccurrence = nextOccurrence; } else { DateTime expectedNextOccurrence; // Track the time that was used to create 'expectedNextOccurrence'. DateTime lastUpdated; if (lastStatus.Last != default(DateTime)) { // If we have a 'Last' value, we know that we used this to calculate 'Next' // in a previous invocation. expectedNextOccurrence = schedule.GetNextOccurrence(lastStatus.Last); lastUpdated = lastStatus.Last; } else if (lastStatus.LastUpdated != default(DateTime)) { // If the trigger has never fired, we won't have 'Last', but we will have // 'LastUpdated', which tells us the last time that we used to calculate 'Next'. expectedNextOccurrence = schedule.GetNextOccurrence(lastStatus.LastUpdated); lastUpdated = lastStatus.LastUpdated; } else { // If we do not have 'LastUpdated' or 'Last', we don't have enough information to // properly calculate 'Next', so we'll calculate it from the current time. expectedNextOccurrence = schedule.GetNextOccurrence(now); lastUpdated = now; } // ensure that the schedule hasn't been updated since the last // time we checked, and if it has, update the status to use the new schedule if (lastStatus.Next != expectedNextOccurrence) { // if the schedule has changed and the next occurrence is in the past, // recalculate it based on the current time as we don't want it to register // immediately as 'past due'. if (now > expectedNextOccurrence) { expectedNextOccurrence = schedule.GetNextOccurrence(now); lastUpdated = now; } lastStatus.Last = default(DateTime); lastStatus.Next = expectedNextOccurrence; lastStatus.LastUpdated = lastUpdated; await UpdateStatusAsync(timerName, lastStatus); } recordedNextOccurrence = lastStatus.Next; } if (now > recordedNextOccurrence) { // if now is after the last next occurrence we recorded, we know we've missed // at least one schedule instance and we are past due return(now - recordedNextOccurrence); } else { // not past due return(TimeSpan.Zero); } }