# Conflicts: # .gitignore # build/NuSpecs/UmbracoCms.Core.nuspec # src/SolutionInfo.cs # src/Umbraco.Core/Configuration/UmbracoSettings/BackOfficeElement.cs # src/Umbraco.Core/Configuration/UmbracoSettings/ContentElement.cs # src/Umbraco.Core/Configuration/UmbracoSettings/IBackOfficeSection.cs # src/Umbraco.Core/Configuration/UmbracoSettings/IContentSection.cs # src/Umbraco.Core/IO/SystemFiles.cs # src/Umbraco.Core/Models/ContentBase.cs # src/Umbraco.Core/Models/Identity/BackOfficeIdentityUser.cs # src/Umbraco.Core/Persistence/UmbracoDatabaseExtensions.cs # src/Umbraco.Core/Runtime/CoreRuntime.cs # src/Umbraco.Core/RuntimeOptions.cs # src/Umbraco.Core/RuntimeState.cs # src/Umbraco.Core/Telemetry/TelemetryMarkerComponent.cs # src/Umbraco.Core/Telemetry/TelemetryMarkerComposer.cs # src/Umbraco.Examine/Umbraco.Examine.csproj # src/Umbraco.Infrastructure/HostedServices/ReportSiteTask.cs # src/Umbraco.Infrastructure/Install/InstallStepCollection.cs # src/Umbraco.Infrastructure/Install/InstallSteps/NewInstallStep.cs # src/Umbraco.Infrastructure/Migrations/Install/DatabaseBuilder.cs # src/Umbraco.Infrastructure/Migrations/Install/DatabaseSchemaCreator.cs # src/Umbraco.Infrastructure/Runtime/SqlMainDomLock.cs # src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/ContentTypeRepositoryTest.cs # src/Umbraco.Tests/Runtimes/CoreRuntimeTests.cs # src/Umbraco.Tests/Runtimes/StandaloneTests.cs # src/Umbraco.Tests/Testing/TestDatabase.cs # src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs # src/Umbraco.Web.UI.Client/src/installer/steps/database.controller.js # src/Umbraco.Web.UI.NetCore/Views/Partials/Grid/Editors/TextString.cshtml # src/Umbraco.Web.UI.NetCore/umbraco/config/lang/da.xml # src/Umbraco.Web.UI.NetCore/umbraco/config/lang/en.xml # src/Umbraco.Web.UI.NetCore/umbraco/config/lang/en_us.xml # src/Umbraco.Web.UI/Umbraco/config/lang/cs.xml # src/Umbraco.Web.UI/config/umbracoSettings.Release.config # src/Umbraco.Web/Composing/CompositionExtensions/Installer.cs # src/Umbraco.Web/Editors/PreviewController.cs # src/Umbraco.Web/Editors/UsersController.cs # src/Umbraco.Web/JavaScript/PreviewInitialize.js # src/Umbraco.Web/Telemetry/TelemetryComponent.cs # src/Umbraco.Web/UmbracoApplication.cs
128 lines
5.1 KiB
C#
128 lines
5.1 KiB
C#
using System;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace Umbraco.Web.Scheduling
|
|
{
|
|
/// <summary>
|
|
/// Provides a base class for recurring background tasks.
|
|
/// </summary>
|
|
/// <remarks>Implement by overriding PerformRun or PerformRunAsync and then IsAsync accordingly,
|
|
/// depending on whether the task is implemented as a sync or async method. Run nor RunAsync are
|
|
/// sealed here as overriding them would break recurrence. And then optionally override
|
|
/// RunsOnShutdown, in order to indicate whether the latched task should run immediately on
|
|
/// shutdown, or just be abandoned (default).</remarks>
|
|
public abstract class RecurringTaskBase : LatchedBackgroundTaskBase
|
|
{
|
|
private readonly IBackgroundTaskRunner<RecurringTaskBase> _runner;
|
|
private readonly long _periodMilliseconds;
|
|
private readonly Timer _timer;
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="RecurringTaskBase"/> class.
|
|
/// </summary>
|
|
/// <param name="runner">The task runner.</param>
|
|
/// <param name="delayMilliseconds">The delay.</param>
|
|
/// <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, long delayMilliseconds, long periodMilliseconds)
|
|
{
|
|
_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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="RecurringTaskBase"/> class.
|
|
/// </summary>
|
|
/// <param name="runner">The task runner.</param>
|
|
/// <param name="delayMilliseconds">The delay.</param>
|
|
/// <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)
|
|
{
|
|
_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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Implements IBackgroundTask.Run().
|
|
/// </summary>
|
|
/// <remarks>Classes inheriting from <c>RecurringTaskBase</c> must implement <c>PerformRun</c>.</remarks>
|
|
public sealed override void Run()
|
|
{
|
|
var shouldRepeat = PerformRun();
|
|
if (shouldRepeat) Repeat();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Implements IBackgroundTask.RunAsync().
|
|
/// </summary>
|
|
/// <remarks>Classes inheriting from <c>RecurringTaskBase</c> must implement <c>PerformRun</c>.</remarks>
|
|
public sealed override async Task RunAsync(CancellationToken token)
|
|
{
|
|
var shouldRepeat = await PerformRunAsync(token);
|
|
if (shouldRepeat) Repeat();
|
|
}
|
|
|
|
private void Repeat()
|
|
{
|
|
// again?
|
|
if (_runner.IsCompleted) return; // fail fast
|
|
|
|
if (_periodMilliseconds == 0) return; // safe
|
|
|
|
Reset(); // re-latch
|
|
|
|
// 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();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Runs the background task.
|
|
/// </summary>
|
|
/// <returns>A value indicating whether to repeat the task.</returns>
|
|
public virtual bool PerformRun()
|
|
{
|
|
throw new NotSupportedException("This task cannot run synchronously.");
|
|
}
|
|
|
|
/// <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 virtual Task<bool> PerformRunAsync(CancellationToken token)
|
|
{
|
|
throw new NotSupportedException("This task cannot run asynchronously.");
|
|
}
|
|
|
|
protected override void DisposeResources()
|
|
{
|
|
base.DisposeResources();
|
|
|
|
// stop the timer
|
|
_timer.Change(Timeout.Infinite, Timeout.Infinite);
|
|
_timer.Dispose();
|
|
}
|
|
}
|
|
}
|