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

View File

@ -94,9 +94,11 @@ namespace Nuclex.Support {
this.threadTerminatedEvent.Dispose(); this.threadTerminatedEvent.Dispose();
this.threadTerminatedEvent = null; this.threadTerminatedEvent = null;
} }
if(this.cancellationTokenSource != null) { lock(this.queueSynchronizationRoot) {
this.cancellationTokenSource.Dispose(); if(this.cancellationTokenSource != null) {
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> /// <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() {
this.cancellationTokenSource.Cancel(); lock(this.queueSynchronizationRoot) {
if(this.cancellationTokenSource != null) {
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