diff --git a/src/Umbraco.Web/Scheduling/BackgroundTaskRunner.cs b/src/Umbraco.Web/Scheduling/BackgroundTaskRunner.cs
index a11606937e..d84a5871af 100644
--- a/src/Umbraco.Web/Scheduling/BackgroundTaskRunner.cs
+++ b/src/Umbraco.Web/Scheduling/BackgroundTaskRunner.cs
@@ -1,24 +1,27 @@
using System;
using System.Collections.Concurrent;
-using System.Globalization;
-using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Hosting;
-using Umbraco.Core.Logging;
using Umbraco.Core.Events;
+using Umbraco.Core.Logging;
namespace Umbraco.Web.Scheduling
{
+ // exists for logging purposes
+ internal class BackgroundTaskRunner
+ { }
+
///
/// Manages a queue of tasks of type and runs them in the background.
///
/// The type of the managed tasks.
/// The task runner is web-aware and will ensure that it shuts down correctly when the AppDomain
/// shuts down (ie is unloaded).
- internal class BackgroundTaskRunner : IBackgroundTaskRunner
+ internal class BackgroundTaskRunner : BackgroundTaskRunner, IBackgroundTaskRunner
where T : class, IBackgroundTask
{
+ private readonly string _logPrefix;
private readonly BackgroundTaskRunnerOptions _options;
private readonly BlockingCollection _tasks = new BlockingCollection();
private readonly object _locker = new object();
@@ -35,13 +38,26 @@ namespace Umbraco.Web.Scheduling
internal event TypedEventHandler, TaskEventArgs> TaskStarting;
internal event TypedEventHandler, TaskEventArgs> TaskCompleted;
internal event TypedEventHandler, TaskEventArgs> TaskCancelled;
+
+ // triggers when the runner stops (but could start again if a task is added to it)
+ internal event TypedEventHandler, EventArgs> Stopped;
+
+ // triggers when the runner completes (no task can be added to it anymore)
internal event TypedEventHandler, EventArgs> Completed;
///
/// Initializes a new instance of the class.
///
public BackgroundTaskRunner()
- : this(new BackgroundTaskRunnerOptions())
+ : this(typeof (T).FullName, new BackgroundTaskRunnerOptions())
+ { }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The name of the runner.
+ public BackgroundTaskRunner(string name)
+ : this(name, new BackgroundTaskRunnerOptions())
{ }
///
@@ -49,9 +65,19 @@ namespace Umbraco.Web.Scheduling
///
/// The set of options.
public BackgroundTaskRunner(BackgroundTaskRunnerOptions options)
+ : this(typeof (T).FullName, options)
+ { }
+
+ ///
+ /// Initializes a new instance of the class with a set of options.
+ ///
+ /// The name of the runner.
+ /// The set of options.
+ public BackgroundTaskRunner(string name, BackgroundTaskRunnerOptions options)
{
if (options == null) throw new ArgumentNullException("options");
_options = options;
+ _logPrefix = "[" + name + "] ";
HostingEnvironment.RegisterObject(this);
@@ -133,7 +159,7 @@ namespace Umbraco.Web.Scheduling
throw new InvalidOperationException("The task runner has completed.");
// add task
- LogHelper.Debug>("Task added {0}", task.GetType);
+ LogHelper.Debug(_logPrefix + "Task added {0}", task.GetType);
_tasks.Add(task);
// start
@@ -154,7 +180,7 @@ namespace Umbraco.Web.Scheduling
if (_isCompleted) return false;
// add task
- LogHelper.Debug>("Task added {0}", task.GetType);
+ LogHelper.Debug(_logPrefix + "Task added {0}", task.GetType);
_tasks.Add(task);
// start
@@ -195,7 +221,7 @@ namespace Umbraco.Web.Scheduling
// create a new token source since this is a new process
_tokenSource = new CancellationTokenSource();
_runningTask = PumpIBackgroundTasks(Task.Factory, _tokenSource.Token);
- LogHelper.Debug>("Starting");
+ LogHelper.Debug(_logPrefix + "Starting");
}
///
@@ -236,7 +262,6 @@ namespace Umbraco.Web.Scheduling
// tasks in the queue will be executed...
if (wait == false) return;
_runningTask.Wait(); // wait for whatever is running to end...
-
}
///
@@ -260,23 +285,24 @@ namespace Umbraco.Web.Scheduling
{
if (token.IsCancellationRequested || _tasks.Count == 0)
{
- LogHelper.Debug>("_isRunning = false");
+ LogHelper.Debug(_logPrefix + "Stopping");
_isRunning = false; // done
if (_options.PreserveRunningTask == false)
_runningTask = null;
- //raise event
- OnCompleted();
return;
}
}
+ OnStopped();
+
// if _runningTask is taskSource.Task then we must keep continuing it,
// not starting a new taskSource, else _runningTask would complete and
// something may be waiting on it
//PumpIBackgroundTasks(factory, token); // restart
- // ReSharper disable once MethodSupportsCancellation // always run
+ // ReSharper disable MethodSupportsCancellation // always run
t.ContinueWithTask(_ => PumpIBackgroundTasks(factory, token)); // restart
+ // ReSharper restore MethodSupportsCancellation
});
Action pump = null;
@@ -288,7 +314,7 @@ namespace Umbraco.Web.Scheduling
if (task != null && task.IsFaulted)
{
var exception = task.Exception;
- LogHelper.Error>("Task runner exception.", exception);
+ LogHelper.Error(_logPrefix + "Task runner exception.", exception);
}
// is it ok to run?
@@ -298,6 +324,7 @@ namespace Umbraco.Web.Scheduling
// the blocking MoveNext will end if token is cancelled or collection is completed
T bgTask;
var hasBgTask = _options.KeepAlive
+ // ReSharper disable once PossibleNullReferenceException
? (bgTask = enumerator.MoveNext() ? enumerator.Current : null) != null // blocking
: _tasks.TryTake(out bgTask); // non-blocking
@@ -343,7 +370,7 @@ namespace Umbraco.Web.Scheduling
return taskSourceContinuing;
}
- private bool TaskSourceCanceled(TaskCompletionSource