OperationQueue now attempts to prevent deep call stack nesting levels by looping when an Operation fired its AsyncEnded event synchronously; the OperationQueue's current operation index is now handled using the Interlocked methods, fixing a possible thread safety issue
git-svn-id: file:///srv/devel/repo-conversion/nusu@170 d2e56fa2-650e-0410-a79f-9358c0239efd
This commit is contained in:
parent
2472c892fb
commit
35b1ca742d
|
@ -18,12 +18,12 @@ License along with this library
|
|||
*/
|
||||
#endregion
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using NUnit.Framework;
|
||||
using NMock2;
|
||||
|
||||
|
@ -354,6 +354,7 @@ namespace Nuclex.Support.Scheduling {
|
|||
|
||||
/// <summary>Mock object factory</summary>
|
||||
private Mockery mockery;
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Tracking
|
||||
|
|
|
@ -20,6 +20,7 @@ License along with this library
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
using Nuclex.Support.Tracking;
|
||||
|
||||
|
@ -125,6 +126,8 @@ namespace Nuclex.Support.Scheduling {
|
|||
/// and launches the operation by calling its Start() method.
|
||||
/// </remarks>
|
||||
private void startCurrentOperation() {
|
||||
do {
|
||||
Thread.MemoryBarrier();
|
||||
OperationType operation = this.children[this.currentOperationIndex].Transaction;
|
||||
|
||||
operation.AsyncEnded += this.asyncOperationEndedDelegate;
|
||||
|
@ -133,7 +136,9 @@ namespace Nuclex.Support.Scheduling {
|
|||
if(progressReporter != null)
|
||||
progressReporter.AsyncProgressChanged += this.asyncOperationProgressChangedDelegate;
|
||||
|
||||
Interlocked.Exchange(ref this.completionStatus, 1);
|
||||
operation.Start();
|
||||
} while(Interlocked.Decrement(ref this.completionStatus) > 0);
|
||||
}
|
||||
|
||||
/// <summary>Disconnects from the current operation and calls its End() method</summary>
|
||||
|
@ -143,6 +148,7 @@ namespace Nuclex.Support.Scheduling {
|
|||
/// counts up the accumulated progress of th e queue.
|
||||
/// </remarks>
|
||||
private void endCurrentOperation() {
|
||||
Thread.MemoryBarrier();
|
||||
OperationType operation = this.children[this.currentOperationIndex].Transaction;
|
||||
|
||||
// Disconnect from the operation's events
|
||||
|
@ -177,15 +183,16 @@ namespace Nuclex.Support.Scheduling {
|
|||
|
||||
// Only jump to the next operation if no exception occured
|
||||
if(this.exception == null) {
|
||||
|
||||
++this.currentOperationIndex;
|
||||
int newIndex = Interlocked.Increment(ref this.currentOperationIndex);
|
||||
Thread.MemoryBarrier();
|
||||
|
||||
// Execute the next operation unless we reached the end of the queue
|
||||
if(this.currentOperationIndex < this.children.Count) {
|
||||
if(newIndex < this.children.Count) {
|
||||
if(Interlocked.Increment(ref this.completionStatus) == 1) {
|
||||
startCurrentOperation();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Either an exception has occured or we reached the end of the operation
|
||||
|
@ -225,6 +232,8 @@ namespace Nuclex.Support.Scheduling {
|
|||
private float completedWeight;
|
||||
/// <summary>Index of the operation currently executing</summary>
|
||||
private int currentOperationIndex;
|
||||
/// <summary>Used to detect when an operation completes synchronously</summary>
|
||||
private int completionStatus;
|
||||
/// <summary>Exception that has occured in the background process</summary>
|
||||
private volatile Exception exception;
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user