private void RemoveFromInfos(JobRunInfo info) { if (info != null && _infos != null) { lock (_infos) { if (_infos.Contains(info)) { _infos.Remove(info); } } } }
private void LaunchNextJob(CancellationToken ct, bool runNow) { if (Configuration.RunState == JobRunState.Automatic || runNow) { var info = new JobRunInfo(new Task <bool>(() => Execute(ct), ct)); info.StartTime = runNow ? DateTime.UtcNow : CalculateNextStartTime(); if (_infos != null) { lock (_infos) { _infos.Add(info); } } Task.Run(() => Run(ct, info)); //does not block here on purpose _logger.Log(string.Format("Job \"{0}\" has been setup to run.", Configuration.Name)); } }
private async Task RunTask(JobRunInfo info) { if (info != null && info.Task != null) { var watch = new Stopwatch(); watch.Start(); bool rc = false; try { info.Task.Start(); Status = JobStatus.Running; info.IsRunning = true; rc = await info.Task; } catch (OperationCanceledException) { _logger.Log(string.Format("Job \"{0}\" canceled.", Configuration.Name), LogMessageSeverity.Warning); } catch (Exception ex) { _logger.Log(string.Format("Job \"{0}\" failed! Error - {1}.", Configuration.Name, ex.Message), LogMessageSeverity.Error); } watch.Stop(); info.CompletedSuccessfully = rc; info.RunDuration = watch.Elapsed; info.IsRunning = false; Status = rc ? JobStatus.Success : JobStatus.Error; string message = rc ? "completed successfully" : "failed to complete successfully"; _logger.Log(string.Format("Job \"{0}\" {1}, run time = {2:hh\\:mm\\:ss}", Configuration.Name, message, watch.Elapsed), rc ? LogMessageSeverity.Information : LogMessageSeverity.Error); } }
//a self relaunching scheduler private async Task Run(CancellationToken ct, JobRunInfo info) { if (info != null) { if (Status != JobStatus.Misconfigured) { try { _logger.Log(string.Format(string.Format("Job \"{0}\" scheduled to start \"{1}\"", Configuration.Name, info.StartTime.ToLocalTime()))); WaitTillDoneOrThrow(ct, info.StartTime); //wait till it's time, while checking for cancel token _logger.Log(string.Format(string.Format("Job \"{0}\" starting at \"{1}\"", Configuration.Name, DateTime.UtcNow.ToLocalTime()))); bool willRun = !HasRunningTask() || Configuration.AllowSimultaneousExecutions; if (!willRun && Configuration.RunImmediatelyIfRunTimeMissed) { _logger.Log(string.Format("Job \"{0}\" missed scheduled execution window, will run immediately after currently executing job.", Configuration.Name), LogMessageSeverity.Warning); Task <bool>[] tasks = null; if (_infos != null) { lock (_infos) { tasks = _infos.Where(j => j.IsRunning).Select(j => j.Task).ToArray(); } } if (tasks != null) { if (tasks.Count() > 0) { Task.WaitAll(tasks); //blocks till all the tasks are done } } willRun = true; } LaunchNextJob(ct, false); //launch the next job no matter what if (willRun) { await RunTask(info); //actually run it! } else { _logger.Log(string.Format("Job \"{0}\" missed scheduled execution window.", Configuration.Name), LogMessageSeverity.Warning); } } catch (OperationCanceledException) { _logger.Log(string.Format("The scheduled job, \"{0}\", has been canceled prior to execution.", Configuration.Name), LogMessageSeverity.Warning); } catch (Exception ex) { _logger.Log(string.Format("The scheduled job, \"{0}\", has encountered an error prior to execution - {1}.", Configuration.Name, ex.Message), LogMessageSeverity.Error); } } else { _logger.Log(string.Format("Job \"{0}\" cannot run because it is misconfigured...", Configuration.Name), LogMessageSeverity.Error); } RemoveFromInfos(info); //need to remove here no matter what, success, failure, cancel, exception, missed window, etc } }