Made the ParallelBackgroundWorker.CancelRunningTasks() and .CancelPendingTasks() methods safe to call after Dispose() (since many usage patterns become much easier with this allowed); Wait() didn't wait for queued tasks (though as long as tasks are queued, there will always be executing tasks, so this is just me being pedantic) - fixed; fixed a race condition in one of the unit tests for the ParallelBackgroundWorker

git-svn-id: file:///srv/devel/repo-conversion/nusu@293 d2e56fa2-650e-0410-a79f-9358c0239efd
This commit is contained in:
Markus Ewald 2014-02-24 11:02:39 +00:00
parent a2b92248b5
commit 0a7306bb58
2 changed files with 41 additions and 6 deletions

View File

@ -95,9 +95,11 @@ namespace Nuclex.Support {
this.WasCancelled = cancellationToken.IsCancellationRequested; this.WasCancelled = cancellationToken.IsCancellationRequested;
if(this.Tasks != null) { if(this.Tasks != null) {
lock(this.Tasks) {
this.Tasks.Add(task); this.Tasks.Add(task);
} }
} }
}
/// <summary>Whether the work tasks should throw exceptions</summary> /// <summary>Whether the work tasks should throw exceptions</summary>
public bool ThrowException; public bool ThrowException;
@ -236,8 +238,10 @@ namespace Nuclex.Support {
waitEvent.Set(); waitEvent.Set();
testWorker.Join(); testWorker.Join();
lock(testWorker.Tasks) {
CollectionAssert.AreEquivalent(tasks, testWorker.Tasks); CollectionAssert.AreEquivalent(tasks, testWorker.Tasks);
} }
}
} // disposes waitEvent } // disposes waitEvent
} }

View File

@ -94,11 +94,13 @@ namespace Nuclex.Support {
this.threadTerminatedEvent.Dispose(); this.threadTerminatedEvent.Dispose();
this.threadTerminatedEvent = null; this.threadTerminatedEvent = null;
} }
lock(this.queueSynchronizationRoot) {
if(this.cancellationTokenSource != null) { if(this.cancellationTokenSource != null) {
this.cancellationTokenSource.Dispose(); this.cancellationTokenSource.Dispose();
this.cancellationTokenSource = null; this.cancellationTokenSource = null;
} }
} }
}
/// <summary>Adds a task for processing by the background worker threads</summary> /// <summary>Adds a task for processing by the background worker threads</summary>
/// <param name="task">Task that will be processed in the background</param> /// <param name="task">Task that will be processed in the background</param>
@ -132,11 +134,21 @@ namespace Nuclex.Support {
} }
/// <summary>Cancels all tasks that are currently executing</summary> /// <summary>Cancels all tasks that are currently executing</summary>
/// <remarks>
/// It is valid to call this method after Dispose()
/// </remarks>
public void CancelRunningTasks() { public void CancelRunningTasks() {
lock(this.queueSynchronizationRoot) {
if(this.cancellationTokenSource != null) {
this.cancellationTokenSource.Cancel(); this.cancellationTokenSource.Cancel();
} }
}
}
/// <summary>Cancels all queued tasks waiting to be executed</summary> /// <summary>Cancels all queued tasks waiting to be executed</summary>
/// <remarks>
/// It is valid to call this method after Dispose()
/// </remarks>
public void CancelPendingTasks() { public void CancelPendingTasks() {
lock(this.queueSynchronizationRoot) { lock(this.queueSynchronizationRoot) {
this.tasks.Clear(); this.tasks.Clear();
@ -166,6 +178,15 @@ namespace Nuclex.Support {
/// True if all tasks have been processed, false if the timeout was reached /// True if all tasks have been processed, false if the timeout was reached
/// </returns> /// </returns>
public bool Wait(int timeoutMilliseconds) { public bool Wait(int timeoutMilliseconds) {
// Wait until the task queue has become empty
while(queuedTaskCount > 0) {
if(this.threadTerminatedEvent.WaitOne(timeoutMilliseconds) == false) {
return false;
}
}
// Now wait until all running tasks have finished
while(Thread.VolatileRead(ref this.runningThreadCount) > 0) { while(Thread.VolatileRead(ref this.runningThreadCount) > 0) {
if(this.threadTerminatedEvent.WaitOne(timeoutMilliseconds) == false) { if(this.threadTerminatedEvent.WaitOne(timeoutMilliseconds) == false) {
return false; return false;
@ -173,6 +194,7 @@ namespace Nuclex.Support {
} }
return true; return true;
} }
/// <summary>Called in a thread to execute a single task</summary> /// <summary>Called in a thread to execute a single task</summary>
@ -219,6 +241,15 @@ namespace Nuclex.Support {
} }
} }
/// <summary>Number of task still waiting to be executed</summary>
private int queuedTaskCount {
get {
lock(this.queueSynchronizationRoot) {
return this.tasks.Count;
}
}
}
/// <summary> /// <summary>
/// Name that will be assigned to the worker threads while they're processing tasks /// Name that will be assigned to the worker threads while they're processing tasks
/// for the parallel background worker /// for the parallel background worker