using System.Threading; using System.Threading.Tasks; namespace Umbraco.Web.Scheduling { /// /// Provides a base class for recurring background tasks. /// /// The type of the managed tasks. internal abstract class RecurringTaskBase : IBackgroundTask where T : class, IBackgroundTask { private readonly IBackgroundTaskRunner _runner; private readonly int _periodMilliseconds; private Timer _timer; private T _recurrent; /// /// Initializes a new instance of the class with a tasks runner and a period. /// /// The task runner. /// The period. /// The task will repeat itself periodically. Use this constructor to create a new task. protected RecurringTaskBase(IBackgroundTaskRunner runner, int periodMilliseconds) { _runner = runner; _periodMilliseconds = periodMilliseconds; } /// /// Initializes a new instance of the class with a source task. /// /// The source task. /// Use this constructor to create a new task from a source task in GetRecurring. protected RecurringTaskBase(RecurringTaskBase source) { _runner = source._runner; _timer = source._timer; _periodMilliseconds = source._periodMilliseconds; } /// /// Implements IBackgroundTask.Run(). /// /// Classes inheriting from RecurringTaskBase must implement PerformRun. public virtual void Run() { PerformRun(); Repeat(); } /// /// Implements IBackgroundTask.RunAsync(). /// /// Classes inheriting from RecurringTaskBase must implement PerformRun. public virtual async Task RunAsync() { await PerformRunAsync(); Repeat(); } private void Repeat() { // again? if (_runner.IsCompleted) return; // fail fast if (_periodMilliseconds == 0) return; _recurrent = GetRecurring(); if (_recurrent == null) { _timer.Dispose(); _timer = null; return; // done } // note // must use the single-parameter constructor on Timer to avoid it from being GC'd // read http://stackoverflow.com/questions/4962172/why-does-a-system-timers-timer-survive-gc-but-not-system-threading-timer _timer = _timer ?? new Timer(_ => _runner.TryAdd(_recurrent)); _timer.Change(_periodMilliseconds, 0); } /// /// Indicates whether the background task can run asynchronously. /// public abstract bool IsAsync { get; } /// /// Runs the background task. /// public abstract void PerformRun(); /// /// Runs the task asynchronously. /// /// A instance representing the execution of the background task. public abstract Task PerformRunAsync(); /// /// Gets a new occurence of the recurring task. /// /// A new task instance to be queued, or null to terminate the recurring task. /// The new task instance must be created via the RecurringTaskBase(RecurringTaskBase{T} source) constructor, /// where source is the current task, eg: return new MyTask(this); protected abstract T GetRecurring(); /// /// Dispose the task. /// public virtual void Dispose() { } } }