diff --git a/Source/Threading/ThreadRunner.cs b/Source/Threading/ThreadRunner.cs
index d0b7cf7..b86e9ab 100644
--- a/Source/Threading/ThreadRunner.cs
+++ b/Source/Threading/ThreadRunner.cs
@@ -28,433 +28,433 @@ using System.Threading.Tasks;
namespace Nuclex.Support.Threading {
- /// Executes actions in a threads
- public abstract class ThreadRunner : IDisposable {
+ /// Executes actions in a threads
+ public abstract class ThreadRunner : IDisposable {
- #region interface IRunner
+ #region interface IRunner
- /// Interface for a background task runner
- private interface IRunner {
+ /// Interface for a background task runner
+ private interface IRunner {
- /// Runs the background task
- void Run();
+ /// Runs the background task
+ void Run();
- /// The runner's cancellation token source
- CancellationTokenSource CancellationTokenSource { get; }
+ /// The runner's cancellation token source
+ CancellationTokenSource CancellationTokenSource { get; }
- }
+ }
- #endregion // interface IRunner
+ #endregion // interface IRunner
- #region struct Runner
+ #region struct Runner
- /// Runs a background task with no parameters
- private struct Runner : IRunner {
+ /// Runs a background task with no parameters
+ private struct Runner : IRunner {
- /// Initializes a new runner
- /// Action the runner will execute
- public Runner(Action action) {
- this.action = action;
- }
+ /// Initializes a new runner
+ /// Action the runner will execute
+ public Runner(Action action) {
+ this.action = action;
+ }
- /// Executes the runner's action
- public void Run() {
- this.action();
- }
+ /// Executes the runner's action
+ public void Run() {
+ this.action();
+ }
- /// The runner's cancellation token source
- public CancellationTokenSource CancellationTokenSource {
- get { return null; }
- }
+ /// The runner's cancellation token source
+ public CancellationTokenSource CancellationTokenSource {
+ get { return null; }
+ }
- /// Action the runner will execute
- private Action action;
+ /// Action the runner will execute
+ private Action action;
- }
+ }
- #endregion // struct Runner
+ #endregion // struct Runner
- #region struct CancellableRunner
+ #region struct CancellableRunner
- /// Runs a background task with no parameters
- private struct CancellableRunner : IRunner {
+ /// Runs a background task with no parameters
+ private struct CancellableRunner : IRunner {
- /// Initializes a new runner
- /// Action the runner will execute
- public CancellableRunner(CancellableAction action) {
- this.action = action;
- this.cancellationTokenSource = new CancellationTokenSource();
- }
+ /// Initializes a new runner
+ /// Action the runner will execute
+ public CancellableRunner(CancellableAction action) {
+ this.action = action;
+ this.cancellationTokenSource = new CancellationTokenSource();
+ }
- /// Executes the runner's action
- public void Run() {
- this.action(this.cancellationTokenSource.Token);
- }
+ /// Executes the runner's action
+ public void Run() {
+ this.action(this.cancellationTokenSource.Token);
+ }
- /// The runner's cancellation token source
- public CancellationTokenSource CancellationTokenSource {
- get { return this.cancellationTokenSource; }
- }
+ /// The runner's cancellation token source
+ public CancellationTokenSource CancellationTokenSource {
+ get { return this.cancellationTokenSource; }
+ }
- /// The runner's cancellation token source
- private CancellationTokenSource cancellationTokenSource;
- /// Action the runner will execute
- private CancellableAction action;
+ /// The runner's cancellation token source
+ private CancellationTokenSource cancellationTokenSource;
+ /// Action the runner will execute
+ private CancellableAction action;
- }
+ }
- #endregion // struct CancellableRunner
+ #endregion // struct CancellableRunner
- #region struct Runner
+ #region struct Runner
- /// Runs a background task with one parameter
- private struct Runner : IRunner {
+ /// Runs a background task with one parameter
+ private struct Runner : IRunner {
- /// Initializes a new runner
- /// Action the runner will execute
- /// Parameter that will be passed to the action
- public Runner(Action action, P1 parameter1) {
- this.action = action;
- this.parameter1 = parameter1;
- }
+ /// Initializes a new runner
+ /// Action the runner will execute
+ /// Parameter that will be passed to the action
+ public Runner(Action action, P1 parameter1) {
+ this.action = action;
+ this.parameter1 = parameter1;
+ }
- /// Executes the runner's action
- public void Run() {
- this.action(this.parameter1);
- }
+ /// Executes the runner's action
+ public void Run() {
+ this.action(this.parameter1);
+ }
- /// The runner's cancellation token source
- public CancellationTokenSource CancellationTokenSource {
- get { return null; }
- }
+ /// The runner's cancellation token source
+ public CancellationTokenSource CancellationTokenSource {
+ get { return null; }
+ }
- /// Action the runner will execute
- private Action action;
- /// Parameter that will be passed to the action
- private P1 parameter1;
+ /// Action the runner will execute
+ private Action action;
+ /// Parameter that will be passed to the action
+ private P1 parameter1;
- }
+ }
- #endregion // struct Runner
-
- #region struct CancellableRunner
-
- /// Runs a background task with one parameter
- private struct CancellableRunner : IRunner {
-
- /// Initializes a new runner
- /// Action the runner will execute
- /// Parameter that will be passed to the action
- public CancellableRunner(CancellableAction action, P1 parameter1) {
- this.action = action;
- this.parameter1 = parameter1;
- this.cancellationTokenSource = new CancellationTokenSource();
- }
-
- /// Executes the runner's action
- public void Run() {
- this.action(this.parameter1, this.cancellationTokenSource.Token);
- }
-
- /// The runner's cancellation token source
- public CancellationTokenSource CancellationTokenSource {
- get { return this.cancellationTokenSource; }
- }
-
- /// The runner's cancellation token source
- private CancellationTokenSource cancellationTokenSource;
- /// Action the runner will execute
- private CancellableAction action;
- /// Parameter that will be passed to the action
- private P1 parameter1;
-
- }
-
- #endregion // struct CancellableRunner
-
- #region struct Runner
-
- /// Runs a background task with one parameter
- private struct Runner : IRunner {
-
- /// Initializes a new runner
- /// Action the runner will execute
- /// First parameter that will be passed to the action
- /// Second parameter that will be passed to the action
- public Runner(Action action, P1 parameter1, P2 parameter2) {
- this.action = action;
- this.parameter1 = parameter1;
- this.parameter2 = parameter2;
- }
-
- /// Executes the runner's action
- public void Run() {
- this.action(this.parameter1, this.parameter2);
- }
-
- /// The runner's cancellation token source
- public CancellationTokenSource CancellationTokenSource {
- get { return null; }
- }
-
- /// Action the runner will execute
- private Action action;
- /// First parameter that will be passed to the action
- private P1 parameter1;
- /// Second parameter that will be passed to the action
- private P2 parameter2;
-
- }
-
- #endregion // struct Runner
-
- #region struct CancellableRunner
-
- /// Runs a background task with one parameter
- private struct CancellableRunner : IRunner {
-
- /// Initializes a new runner
- /// Action the runner will execute
- /// First parameter that will be passed to the action
- /// Second parameter that will be passed to the action
- public CancellableRunner(CancellableAction action, P1 parameter1, P2 parameter2) {
- this.action = action;
- this.parameter1 = parameter1;
- this.parameter2 = parameter2;
- this.cancellationTokenSource = new CancellationTokenSource();
- }
-
- /// Executes the runner's action
- public void Run() {
- this.action(this.parameter1, this.parameter2, this.cancellationTokenSource.Token);
- }
-
- /// The runner's cancellation token source
- public CancellationTokenSource CancellationTokenSource {
- get { return this.cancellationTokenSource; }
- }
-
- /// The runner's cancellation token source
- private CancellationTokenSource cancellationTokenSource;
- /// Action the runner will execute
- private CancellableAction action;
- /// First parameter that will be passed to the action
- private P1 parameter1;
- /// Second parameter that will be passed to the action
- private P2 parameter2;
-
- }
-
- #endregion // struct CancellableRunner
-
- /// Initializes a new background processing handler
- public ThreadRunner() {
- this.executeQueuedRunnersInThreadDelegate = new Action(executeQueuedRunnersInThread);
- this.queuedRunners = new ConcurrentQueue();
- }
-
- ///
- /// Immediately cancels all operations and releases the resources used by the instance
- ///
- public void Dispose() {
- Dispose(timeoutMilliseconds: 2500);
- }
-
- ///
- /// Immediately cancels all operations and releases the resources used by the instance
- ///
- ///
- /// Time to wait for the background tasks before dropping the tasks unfinished
- ///
- public void Dispose(int timeoutMilliseconds) {
- CancelAllBackgroundOperations();
-
- Task currentTask;
- lock(this) {
- currentTask = this.currentTask;
- }
-
- if(currentTask != null) {
- if(!currentTask.Wait(timeoutMilliseconds)) {
- Debug.Assert(false, "Task does not support cancellation or did not cancel in time");
- }
- lock(this) {
- this.currentTask = null;
- IsBusy = false;
- }
- }
- }
-
- /// Whether the view model is currently busy executing a task
- public bool IsBusy {
- get { return this.isBusy; }
- private set {
- if(value != this.isBusy) {
- this.isBusy = value;
- BusyChanged();
- }
- }
- }
-
- /// Reports an error
- /// Error that will be reported
- protected abstract void ReportError(Exception exception);
-
- /// Called when the status of the busy flag changes
- protected abstract void BusyChanged();
-
- /// Executes the specified operation in the background
- /// Action that will be executed in the background
- public void RunInBackground(Action action) {
- this.queuedRunners.Enqueue(new Runner(action));
- startBackgroundProcessingIfNecessary();
- }
-
- /// Executes the specified operation in the background
- /// Action that will be executed in the background
- public void RunInBackground(CancellableAction action) {
- this.queuedRunners.Enqueue(new CancellableRunner(action));
- startBackgroundProcessingIfNecessary();
- }
-
- /// Executes the specified operation in the background
- /// Action that will be executed in the background
- /// Parameter that will be passed to the action
- public void RunInBackground(Action action, P1 parameter1) {
- this.queuedRunners.Enqueue(new Runner(action, parameter1));
- startBackgroundProcessingIfNecessary();
- }
-
- /// Executes the specified operation in the background
- /// Action that will be executed in the background
- /// Parameter that will be passed to the action
- public void RunInBackground(CancellableAction action, P1 parameter1) {
- this.queuedRunners.Enqueue(new CancellableRunner(action, parameter1));
- startBackgroundProcessingIfNecessary();
- }
-
- /// Executes the specified operation in the background
- /// Action that will be executed in the background
- /// First parameter that will be passed to the action
- /// Second parameter that will be passed to the action
- public void RunInBackground(Action action, P1 parameter1, P2 parameter2) {
- this.queuedRunners.Enqueue(new Runner(action, parameter1, parameter2));
- startBackgroundProcessingIfNecessary();
- }
-
- /// Executes the specified operation in the background
- /// Action that will be executed in the background
- /// First parameter that will be passed to the action
- /// Second parameter that will be passed to the action
- public void RunInBackground(
- CancellableAction action, P1 parameter1, P2 parameter2
- ) {
- this.queuedRunners.Enqueue(new CancellableRunner(action, parameter1, parameter2));
- startBackgroundProcessingIfNecessary();
- }
-
- /// Cancels the currently running background operation
- public void CancelBackgroundOperation() {
- IRunner currentRunner = this.currentRunner;
- if(currentRunner != null) {
- CancellationTokenSource cancellationTokenSource = currentRunner.CancellationTokenSource;
- if(cancellationTokenSource != null) {
- cancellationTokenSource.Cancel();
- }
- }
- }
-
- /// Cancels all queued and the currently running background operation
- public void CancelAllBackgroundOperations() {
- IRunner runner;
- while(this.queuedRunners.TryDequeue(out runner)) {
- CancellationTokenSource cancellationTokenSource = runner.CancellationTokenSource;
- if(cancellationTokenSource != null) {
- cancellationTokenSource.Cancel();
- }
- }
-
- CancelBackgroundOperation();
- }
-
- /// Whether the background operation has been cancelled
- //[Obsolete("Please use a method accepting a cancellation token instead of using this")]
- public bool IsBackgroundOperationCancelled {
- get {
- IRunner currentRunner = this.currentRunner;
- if(currentRunner != null) {
- return currentRunner.CancellationTokenSource.IsCancellationRequested;
- } else {
- return false;
- }
- }
- }
-
- /// Throws an exception if the background operation was cancelled
- //[Obsolete("Please use a method accepting a cancellation token instead of using this")]
- public void ThrowIfBackgroundOperationCancelled() {
- IRunner currentRunner = this.currentRunner;
- if(currentRunner != null) {
- currentRunner.CancellationTokenSource.Token.ThrowIfCancellationRequested();
- }
- }
-
- /// Executes the queued runners in the background
- private void executeQueuedRunnersInThread() {
- IsBusy = true;
-
- IRunner runner;
- while(this.queuedRunners.TryDequeue(out runner)) {
- try {
- this.currentRunner = runner;
- runner.Run();
- }
- catch(OperationCanceledException) {
- // Ignore
- }
- catch(Exception exception) {
- this.currentRunner = null; // When the error is reported this should already be null
- ReportError(exception);
- }
- this.currentRunner = null;
- }
-
- lock(this) {
- this.currentTask = null;
- IsBusy = false;
- }
- }
-
- /// Starts the background processing thread, if needed
- private void startBackgroundProcessingIfNecessary() {
- Task currentTask;
-
- lock(this) {
- if(this.currentTask == null) {
- currentTask = new Task(this.executeQueuedRunnersInThreadDelegate);
- this.currentTask = currentTask;
- } else {
- return; // Task is already running
- }
- }
-
- // Start the task outside of the lock statement so that when the thread starts to run,
- // it is guaranteed to read the currentTask variable as the task we just created.
- currentTask.Start();
- }
-
- /// Whether the view model is currently busy executing a task
- private volatile bool isBusy;
- /// Delegate for the executedQueuedRunnersInThread() method
- private Action executeQueuedRunnersInThreadDelegate;
- /// Queued background operations
- private ConcurrentQueue queuedRunners;
- /// Runner currently executing in the background
- private volatile IRunner currentRunner;
- /// Task that is currently executing the runners
- private Task currentTask;
-
- }
+ #endregion // struct Runner
+
+ #region struct CancellableRunner
+
+ /// Runs a background task with one parameter
+ private struct CancellableRunner : IRunner {
+
+ /// Initializes a new runner
+ /// Action the runner will execute
+ /// Parameter that will be passed to the action
+ public CancellableRunner(CancellableAction action, P1 parameter1) {
+ this.action = action;
+ this.parameter1 = parameter1;
+ this.cancellationTokenSource = new CancellationTokenSource();
+ }
+
+ /// Executes the runner's action
+ public void Run() {
+ this.action(this.parameter1, this.cancellationTokenSource.Token);
+ }
+
+ /// The runner's cancellation token source
+ public CancellationTokenSource CancellationTokenSource {
+ get { return this.cancellationTokenSource; }
+ }
+
+ /// The runner's cancellation token source
+ private CancellationTokenSource cancellationTokenSource;
+ /// Action the runner will execute
+ private CancellableAction action;
+ /// Parameter that will be passed to the action
+ private P1 parameter1;
+
+ }
+
+ #endregion // struct CancellableRunner
+
+ #region struct Runner
+
+ /// Runs a background task with one parameter
+ private struct Runner : IRunner {
+
+ /// Initializes a new runner
+ /// Action the runner will execute
+ /// First parameter that will be passed to the action
+ /// Second parameter that will be passed to the action
+ public Runner(Action action, P1 parameter1, P2 parameter2) {
+ this.action = action;
+ this.parameter1 = parameter1;
+ this.parameter2 = parameter2;
+ }
+
+ /// Executes the runner's action
+ public void Run() {
+ this.action(this.parameter1, this.parameter2);
+ }
+
+ /// The runner's cancellation token source
+ public CancellationTokenSource CancellationTokenSource {
+ get { return null; }
+ }
+
+ /// Action the runner will execute
+ private Action action;
+ /// First parameter that will be passed to the action
+ private P1 parameter1;
+ /// Second parameter that will be passed to the action
+ private P2 parameter2;
+
+ }
+
+ #endregion // struct Runner
+
+ #region struct CancellableRunner
+
+ /// Runs a background task with one parameter
+ private struct CancellableRunner : IRunner {
+
+ /// Initializes a new runner
+ /// Action the runner will execute
+ /// First parameter that will be passed to the action
+ /// Second parameter that will be passed to the action
+ public CancellableRunner(CancellableAction action, P1 parameter1, P2 parameter2) {
+ this.action = action;
+ this.parameter1 = parameter1;
+ this.parameter2 = parameter2;
+ this.cancellationTokenSource = new CancellationTokenSource();
+ }
+
+ /// Executes the runner's action
+ public void Run() {
+ this.action(this.parameter1, this.parameter2, this.cancellationTokenSource.Token);
+ }
+
+ /// The runner's cancellation token source
+ public CancellationTokenSource CancellationTokenSource {
+ get { return this.cancellationTokenSource; }
+ }
+
+ /// The runner's cancellation token source
+ private CancellationTokenSource cancellationTokenSource;
+ /// Action the runner will execute
+ private CancellableAction action;
+ /// First parameter that will be passed to the action
+ private P1 parameter1;
+ /// Second parameter that will be passed to the action
+ private P2 parameter2;
+
+ }
+
+ #endregion // struct CancellableRunner
+
+ /// Initializes a new background processing handler
+ public ThreadRunner() {
+ this.executeQueuedRunnersInThreadDelegate = new Action(executeQueuedRunnersInThread);
+ this.queuedRunners = new ConcurrentQueue();
+ }
+
+ ///
+ /// Immediately cancels all operations and releases the resources used by the instance
+ ///
+ public void Dispose() {
+ Dispose(timeoutMilliseconds: 2500);
+ }
+
+ ///
+ /// Immediately cancels all operations and releases the resources used by the instance
+ ///
+ ///
+ /// Time to wait for the background tasks before dropping the tasks unfinished
+ ///
+ public void Dispose(int timeoutMilliseconds) {
+ CancelAllBackgroundOperations();
+
+ Task currentTask;
+ lock(this) {
+ currentTask = this.currentTask;
+ }
+
+ if(currentTask != null) {
+ if(!currentTask.Wait(timeoutMilliseconds)) {
+ Debug.Assert(false, "Task does not support cancellation or did not cancel in time");
+ }
+ lock(this) {
+ this.currentTask = null;
+ IsBusy = false;
+ }
+ }
+ }
+
+ /// Whether the view model is currently busy executing a task
+ public bool IsBusy {
+ get { return this.isBusy; }
+ private set {
+ if(value != this.isBusy) {
+ this.isBusy = value;
+ BusyChanged();
+ }
+ }
+ }
+
+ /// Reports an error
+ /// Error that will be reported
+ protected abstract void ReportError(Exception exception);
+
+ /// Called when the status of the busy flag changes
+ protected abstract void BusyChanged();
+
+ /// Executes the specified operation in the background
+ /// Action that will be executed in the background
+ public void RunInBackground(Action action) {
+ this.queuedRunners.Enqueue(new Runner(action));
+ startBackgroundProcessingIfNecessary();
+ }
+
+ /// Executes the specified operation in the background
+ /// Action that will be executed in the background
+ public void RunInBackground(CancellableAction action) {
+ this.queuedRunners.Enqueue(new CancellableRunner(action));
+ startBackgroundProcessingIfNecessary();
+ }
+
+ /// Executes the specified operation in the background
+ /// Action that will be executed in the background
+ /// Parameter that will be passed to the action
+ public void RunInBackground(Action action, P1 parameter1) {
+ this.queuedRunners.Enqueue(new Runner(action, parameter1));
+ startBackgroundProcessingIfNecessary();
+ }
+
+ /// Executes the specified operation in the background
+ /// Action that will be executed in the background
+ /// Parameter that will be passed to the action
+ public void RunInBackground(CancellableAction action, P1 parameter1) {
+ this.queuedRunners.Enqueue(new CancellableRunner(action, parameter1));
+ startBackgroundProcessingIfNecessary();
+ }
+
+ /// Executes the specified operation in the background
+ /// Action that will be executed in the background
+ /// First parameter that will be passed to the action
+ /// Second parameter that will be passed to the action
+ public void RunInBackground(Action action, P1 parameter1, P2 parameter2) {
+ this.queuedRunners.Enqueue(new Runner(action, parameter1, parameter2));
+ startBackgroundProcessingIfNecessary();
+ }
+
+ /// Executes the specified operation in the background
+ /// Action that will be executed in the background
+ /// First parameter that will be passed to the action
+ /// Second parameter that will be passed to the action
+ public void RunInBackground(
+ CancellableAction action, P1 parameter1, P2 parameter2
+ ) {
+ this.queuedRunners.Enqueue(new CancellableRunner(action, parameter1, parameter2));
+ startBackgroundProcessingIfNecessary();
+ }
+
+ /// Cancels the currently running background operation
+ public void CancelBackgroundOperation() {
+ IRunner currentRunner = this.currentRunner;
+ if(currentRunner != null) {
+ CancellationTokenSource cancellationTokenSource = currentRunner.CancellationTokenSource;
+ if(cancellationTokenSource != null) {
+ cancellationTokenSource.Cancel();
+ }
+ }
+ }
+
+ /// Cancels all queued and the currently running background operation
+ public void CancelAllBackgroundOperations() {
+ IRunner runner;
+ while(this.queuedRunners.TryDequeue(out runner)) {
+ CancellationTokenSource cancellationTokenSource = runner.CancellationTokenSource;
+ if(cancellationTokenSource != null) {
+ cancellationTokenSource.Cancel();
+ }
+ }
+
+ CancelBackgroundOperation();
+ }
+
+ /// Whether the background operation has been cancelled
+ //[Obsolete("Please use a method accepting a cancellation token instead of using this")]
+ public bool IsBackgroundOperationCancelled {
+ get {
+ IRunner currentRunner = this.currentRunner;
+ if(currentRunner != null) {
+ return currentRunner.CancellationTokenSource.IsCancellationRequested;
+ } else {
+ return false;
+ }
+ }
+ }
+
+ /// Throws an exception if the background operation was cancelled
+ //[Obsolete("Please use a method accepting a cancellation token instead of using this")]
+ public void ThrowIfBackgroundOperationCancelled() {
+ IRunner currentRunner = this.currentRunner;
+ if(currentRunner != null) {
+ currentRunner.CancellationTokenSource.Token.ThrowIfCancellationRequested();
+ }
+ }
+
+ /// Executes the queued runners in the background
+ private void executeQueuedRunnersInThread() {
+ IsBusy = true;
+
+ IRunner runner;
+ while(this.queuedRunners.TryDequeue(out runner)) {
+ try {
+ this.currentRunner = runner;
+ runner.Run();
+ }
+ catch(OperationCanceledException) {
+ // Ignore
+ }
+ catch(Exception exception) {
+ this.currentRunner = null; // When the error is reported this should already be null
+ ReportError(exception);
+ }
+ this.currentRunner = null;
+ }
+
+ lock(this) {
+ this.currentTask = null;
+ IsBusy = false;
+ }
+ }
+
+ /// Starts the background processing thread, if needed
+ private void startBackgroundProcessingIfNecessary() {
+ Task currentTask;
+
+ lock(this) {
+ if(this.currentTask == null) {
+ currentTask = new Task(this.executeQueuedRunnersInThreadDelegate);
+ this.currentTask = currentTask;
+ } else {
+ return; // Task is already running
+ }
+ }
+
+ // Start the task outside of the lock statement so that when the thread starts to run,
+ // it is guaranteed to read the currentTask variable as the task we just created.
+ currentTask.Start();
+ }
+
+ /// Whether the view model is currently busy executing a task
+ private volatile bool isBusy;
+ /// Delegate for the executedQueuedRunnersInThread() method
+ private Action executeQueuedRunnersInThreadDelegate;
+ /// Queued background operations
+ private ConcurrentQueue queuedRunners;
+ /// Runner currently executing in the background
+ private volatile IRunner currentRunner;
+ /// Task that is currently executing the runners
+ private Task currentTask;
+
+ }
} // namespace Nuclex.Support.Threading