From 35b1ca742d5fafd7c8f961e4fc48927fd4602e79 Mon Sep 17 00:00:00 2001 From: Markus Ewald Date: Fri, 28 Aug 2009 20:27:15 +0000 Subject: [PATCH] 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 --- Source/Scheduling/OperationQueue.Test.cs | 5 ++-- Source/Scheduling/OperationQueue.cs | 33 +++++++++++++++--------- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/Source/Scheduling/OperationQueue.Test.cs b/Source/Scheduling/OperationQueue.Test.cs index 3dd160f..9aeab79 100644 --- a/Source/Scheduling/OperationQueue.Test.cs +++ b/Source/Scheduling/OperationQueue.Test.cs @@ -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 { /// Mock object factory private Mockery mockery; + } } // namespace Nuclex.Support.Tracking diff --git a/Source/Scheduling/OperationQueue.cs b/Source/Scheduling/OperationQueue.cs index 7155fc5..3b9a896 100644 --- a/Source/Scheduling/OperationQueue.cs +++ b/Source/Scheduling/OperationQueue.cs @@ -20,6 +20,7 @@ License along with this library using System; using System.Collections.Generic; +using System.Threading; using Nuclex.Support.Tracking; @@ -125,24 +126,29 @@ namespace Nuclex.Support.Scheduling { /// and launches the operation by calling its Start() method. /// private void startCurrentOperation() { - OperationType operation = this.children[this.currentOperationIndex].Transaction; + do { + Thread.MemoryBarrier(); + OperationType operation = this.children[this.currentOperationIndex].Transaction; - operation.AsyncEnded += this.asyncOperationEndedDelegate; + operation.AsyncEnded += this.asyncOperationEndedDelegate; - IProgressReporter progressReporter = operation as IProgressReporter; - if(progressReporter != null) - progressReporter.AsyncProgressChanged += this.asyncOperationProgressChangedDelegate; + IProgressReporter progressReporter = operation as IProgressReporter; + if(progressReporter != null) + progressReporter.AsyncProgressChanged += this.asyncOperationProgressChangedDelegate; - operation.Start(); + Interlocked.Exchange(ref this.completionStatus, 1); + operation.Start(); + } while(Interlocked.Decrement(ref this.completionStatus) > 0); } /// Disconnects from the current operation and calls its End() method /// /// This unsubscribes the queue from the current operation's events, calls End() /// on the operation and, if the operation didn't have an exception to report, - /// counts up the accumulated progress of the queue. + /// counts up the accumulated progress of th e queue. /// 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) { - startCurrentOperation(); + 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; /// Index of the operation currently executing private int currentOperationIndex; + /// Used to detect when an operation completes synchronously + private int completionStatus; /// Exception that has occured in the background process private volatile Exception exception;