public Task <bool> Run(ScheduledTask task, IProgress <SboJobProgress> progress = null, bool force = false) { Task <bool> returnTask = Task.FromResult(false); try { SboTransaction.Start(); var lockResult = SboDistributedLock.GetLock($"SboTaskScheduler.{task.Name}"); if (lockResult != 0) { SboTransaction.Rollback(); return(returnTask); } var lastOccurrence = SboAddon.Instance.Settings.GetValueOrDefault <DateTime?>("taskscheduler.lastoccurrence." + task.Name); var nextOccurrence = SboAddon.Instance.Settings.GetValueOrDefault <DateTime?>("taskscheduler.nextoccurrence." + task.Name); var synchronicity = task.IsAsync ? "asynchronous" : "synchronous"; Logger.Trace( $"Checking if task {task.Name} is applicable, last occurrence {lastOccurrence} - next occurrence {nextOccurrence}"); if (force || !nextOccurrence.HasValue || nextOccurrence.Value < DateTime.Now) { // Set new values lastOccurrence = DateTime.Now; nextOccurrence = task.Schedule.GetNextOccurrence(lastOccurrence.Value); SboAddon.Instance.Settings.SetValue("taskscheduler.lastoccurrence." + task.Name, lastOccurrence.Value); SboAddon.Instance.Settings.SetValue("taskscheduler.nextoccurrence." + task.Name, nextOccurrence.Value); SboTransaction.Commit(); // Run task var cancellationToken = CancellationToken.None; var task1 = task; var progressObject = progress; returnTask = Task.Factory.StartNew(async() => { Logger.Info($@"Running {synchronicity} task {task.Name}."); var stopwatch = Stopwatch.StartNew(); try { task.Action?.Invoke(progressObject); if (task.AsyncAction != null) { await task.AsyncAction(progressObject); } } catch (Exception e) { Logger.Error(e, $"Unhandled exception occurred running scheduled task {task.Name}"); } stopwatch.Stop(); Logger.Info( $"Completed task {task1.Name} in {stopwatch.Elapsed}. Next occurrence {nextOccurrence}"); SboAddonTracker.TrackEvent("ScheduledTaskRun", new Dictionary <string, string> { ["TaskName"] = task1.Name }, new Dictionary <string, double> { ["Duration"] = stopwatch.ElapsedMilliseconds }); return(true); }, cancellationToken, TaskCreationOptions.None, task.IsAsync ? TaskScheduler.Default : SboAddon.Instance.UiTaskScheduler).Unwrap(); } else if (SboAddon.Instance.Company.InTransaction) { SboTransaction.Commit(); } task.LastOccurrence = lastOccurrence; } catch (Exception e) { Logger.Error(e, "Unhandled exception occurred running scheduled tasks"); try { SboTransaction.Rollback(); } catch (Exception) { } } return(returnTask); }