Files
Umbraco-CMS/src/Umbraco.Core/Scheduling/RecurringTaskBase.cs
Bjarke Berg a54e10bc13 Merge remote-tracking branch 'origin/v8/dev' into netcore/feature/merge-v8-18-01-2021
# 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
2021-01-18 16:06:23 +01:00

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