Files
Umbraco-CMS/src/Umbraco.Web/Scheduling/RecurringTaskBase.cs

96 lines
3.5 KiB
C#
Raw Normal View History

2015-02-06 16:10:34 +01:00
using System.Threading;
using System.Threading.Tasks;
namespace Umbraco.Web.Scheduling
{
/// <summary>
/// Provides a base class for recurring background tasks.
/// </summary>
internal abstract class RecurringTaskBase : LatchedBackgroundTaskBase
2015-02-06 16:10:34 +01:00
{
private readonly IBackgroundTaskRunner<RecurringTaskBase> _runner;
2015-02-06 16:10:34 +01:00
private readonly int _periodMilliseconds;
private readonly Timer _timer;
private bool _disposed;
2015-02-06 16:10:34 +01:00
/// <summary>
/// Initializes a new instance of the <see cref="RecurringTaskBase"/> class.
2015-02-06 16:10:34 +01:00
/// </summary>
/// <param name="runner">The task runner.</param>
/// <param name="delayMilliseconds">The delay.</param>
2015-02-06 16:10:34 +01:00
/// <param name="periodMilliseconds">The period.</param>
/// <remarks>The task will repeat itself periodically. Use this constructor to create a new task.</remarks>
protected RecurringTaskBase(IBackgroundTaskRunner<RecurringTaskBase> runner, int delayMilliseconds, int periodMilliseconds)
2015-02-06 16:10:34 +01:00
{
_runner = runner;
_periodMilliseconds = periodMilliseconds;
// 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 = new Timer(_ => Release());
_timer.Change(delayMilliseconds, 0);
2015-02-06 16:10:34 +01:00
}
/// <summary>
/// Implements IBackgroundTask.Run().
/// </summary>
/// <remarks>Classes inheriting from <c>RecurringTaskBase</c> must implement <c>PerformRun</c>.</remarks>
public override void Run()
2015-02-06 16:10:34 +01:00
{
var shouldRepeat = PerformRun();
if (shouldRepeat) Repeat();
2015-02-06 16:10:34 +01:00
}
/// <summary>
/// Implements IBackgroundTask.RunAsync().
/// </summary>
/// <remarks>Classes inheriting from <c>RecurringTaskBase</c> must implement <c>PerformRun</c>.</remarks>
public override async Task RunAsync(CancellationToken token)
2015-02-06 16:10:34 +01:00
{
var shouldRepeat = await PerformRunAsync(token);
if (shouldRepeat) Repeat();
2015-02-06 16:10:34 +01:00
}
private void Repeat()
{
// again?
if (_runner.IsCompleted) return; // fail fast
if (_periodMilliseconds == 0) return; // safe
2015-02-06 16:10:34 +01:00
Reset(); // re-latch
2015-02-06 16:10:34 +01:00
// try to add again (may fail if runner has completed)
// if added, re-start the timer, else kill it
if (_runner.TryAdd(this))
_timer.Change(_periodMilliseconds, 0);
else
Dispose();
2015-02-06 16:10:34 +01:00
}
/// <summary>
/// Runs the background task.
/// </summary>
/// <returns>A value indicating whether to repeat the task.</returns>
public abstract bool PerformRun();
2015-02-06 16:10:34 +01:00
/// <summary>
/// Runs the task asynchronously.
/// </summary>
/// <param name="token">A cancellation token.</param>
/// <returns>A <see cref="Task{T}"/> instance representing the execution of the background task,
/// and returning a value indicating whether to repeat the task.</returns>
public abstract Task<bool> PerformRunAsync(CancellationToken token);
2015-02-06 16:10:34 +01:00
protected override void DisposeResources()
{
base.DisposeResources();
2015-02-06 16:10:34 +01:00
// stop the timer
_timer.Change(Timeout.Infinite, Timeout.Infinite);
_timer.Dispose();
}
2015-02-06 16:10:34 +01:00
}
}