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,7 +95,9 @@ namespace Nuclex.Support {
this.WasCancelled = cancellationToken.IsCancellationRequested;
if(this.Tasks != null) {
this.Tasks.Add(task);
lock(this.Tasks) {
this.Tasks.Add(task);
}
}
}
@ -236,7 +238,9 @@ namespace Nuclex.Support {
waitEvent.Set();
testWorker.Join();
CollectionAssert.AreEquivalent(tasks, testWorker.Tasks);
lock(testWorker.Tasks) {
CollectionAssert.AreEquivalent(tasks, testWorker.Tasks);
}
}
} // disposes waitEvent
}

View File

@ -94,9 +94,11 @@ namespace Nuclex.Support {
this.threadTerminatedEvent.Dispose();
this.threadTerminatedEvent = null;
}
if(this.cancellationTokenSource != null) {
this.cancellationTokenSource.Dispose();
this.cancellationTokenSource = null;
lock(this.queueSynchronizationRoot) {
if(this.cancellationTokenSource != null) {
this.cancellationTokenSource.Dispose();
this.cancellationTokenSource = null;
}
}
}
@ -132,11 +134,21 @@ namespace Nuclex.Support {
}
/// <summary>Cancels all tasks that are currently executing</summary>
/// <remarks>
/// It is valid to call this method after Dispose()
/// </remarks>
public void CancelRunningTasks() {
this.cancellationTokenSource.Cancel();
lock(this.queueSynchronizationRoot) {
if(this.cancellationTokenSource != null) {
this.cancellationTokenSource.Cancel();
}
}
}
/// <summary>Cancels all queued tasks waiting to be executed</summary>
/// <remarks>
/// It is valid to call this method after Dispose()
/// </remarks>
public void CancelPendingTasks() {
lock(this.queueSynchronizationRoot) {
this.tasks.Clear();
@ -166,6 +178,15 @@ namespace Nuclex.Support {
/// True if all tasks have been processed, false if the timeout was reached
/// </returns>
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) {
if(this.threadTerminatedEvent.WaitOne(timeoutMilliseconds) == false) {
return false;
@ -173,6 +194,7 @@ namespace Nuclex.Support {
}
return true;
}
/// <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>
/// Name that will be assigned to the worker threads while they're processing tasks
/// for the parallel background worker