/// <summary> /// <see cref="TimerCallback"/> to be called every tick. Checks validity of the tick, performs workload and schedues the next tick. /// </summary> /// <param name="state"></param> private static void OnTick(object state) { Logger.LogTick(); DateTime now = DateTime.Now; // Recalculate next run dates if the last tick was more than 90 seconds ago or if it is in the future. // Such clock skew implies manual time change, huge NTP leap, machine awakened from hibernation/suspension or some other potentially unsafe state. if (now - _lastTick > _skewLimit || now < _lastTick) { Logger.LogTickSkew(); foreach (Task task in Config.Tasks.Values) { task.RefreshNextRunDate(); } LogCleaner.Start(); LogCleaner.RefreshNextRunDate(); } else { DoTick(now); } // The next tick time has to be readjusted often to maintain the precision of the scheduled tasks. // System.Threading.Timer is ridiculously imprecise. The best case scenario skews the timer by 1 ms per minute (1 minute per ~100 days). // An alternative would be to create a custom timer implementaion based on high-precision multimedia timers, but // System.Threading.Timer is simple to use and guaranteed to be supported pretty much everywhere, so it's good enough. SetNextTick(now); }
/// <summary> /// Logs the values and tasks present in <see cref="Config"/>. /// </summary> public static void LogConfig() { if (Writer != null) { Log(string.Format("[Config] Got log retention period {0} days", Config.LogRetention)); LogCleaner.RefreshNextRunDate(); // Produces log cleaner log line foreach (Task task in Config.Tasks.Values) { task.RefreshNextRunDate(); // Produces task detail log line } } }
/// <summary> /// Performs workload during normal tick. Runs <see cref="Task"/> or <see cref="LogCleaner"/> if they are scheduled to be run in the current tick. /// </summary> /// <param name="now"></param> private static void DoTick(DateTime now) { foreach (Task task in Config.Tasks.Values) { if (task.NextRunDate <= now) { task.Start(); task.RefreshNextRunDate(); } } if (LogCleaner.NextRunDate <= now) { LogCleaner.Start(); LogCleaner.RefreshNextRunDate(); } }