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()
{ }
}
}