diff --git a/Source/Scheduling/QueueOperation.Test.cs b/Source/Scheduling/QueueOperation.Test.cs index 403c1f1..1f0da41 100644 --- a/Source/Scheduling/QueueOperation.Test.cs +++ b/Source/Scheduling/QueueOperation.Test.cs @@ -109,7 +109,7 @@ namespace Nuclex.Support.Scheduling { /// Moves the operation into the ended state with an exception /// Exception public void SetEnded(Exception exception) { - SetException(exception); + this.exception = exception; OnAsyncEnded(); } @@ -121,6 +121,18 @@ namespace Nuclex.Support.Scheduling { OnAsyncProgressUpdated(progress); } + /// + /// Allows the specific request implementation to re-throw an exception if + /// the background process finished unsuccessfully + /// + protected override void ReraiseExceptions() { + if(this.exception != null) + throw this.exception; + } + + /// Exception that has occured in the background process + private volatile Exception exception; + } #endregion // class TestOperation diff --git a/Source/Scheduling/QueueOperation.cs b/Source/Scheduling/QueueOperation.cs index 5b91eff..d810919 100644 --- a/Source/Scheduling/QueueOperation.cs +++ b/Source/Scheduling/QueueOperation.cs @@ -84,6 +84,15 @@ namespace Nuclex.Support.Scheduling { startCurrentOperation(); } + /// + /// Allows the specific request implementation to re-throw an exception if + /// the background process finished unsuccessfully + /// + protected override void ReraiseExceptions() { + if(this.exception != null) + throw this.exception; + } + /// Prepares the current operation and calls its Begin() method /// /// This subscribes the queue to the events of to the current operation @@ -121,7 +130,7 @@ namespace Nuclex.Support.Scheduling { OnAsyncProgressUpdated(this.completedWeight / this.totalWeight); } catch(Exception exception) { - SetException(exception); + this.exception = exception; } } @@ -135,7 +144,7 @@ namespace Nuclex.Support.Scheduling { endCurrentOperation(); // Only jump to the next operation if no exception occured - if(OccuredException == null) { + if(this.exception == null) { ++this.currentOperationIndex; @@ -183,6 +192,8 @@ namespace Nuclex.Support.Scheduling { private float completedWeight; /// Index of the operation currently executing private int currentOperationIndex; + /// Exception that has occured in the background process + private volatile Exception exception; } diff --git a/Source/Scheduling/ThreadOperation.cs b/Source/Scheduling/ThreadOperation.cs index 0adac8d..22624b6 100644 --- a/Source/Scheduling/ThreadOperation.cs +++ b/Source/Scheduling/ThreadOperation.cs @@ -70,15 +70,26 @@ namespace Nuclex.Support.Scheduling { Execute(); } catch(Exception exception) { - SetException(exception); + this.exception = exception; } finally { OnAsyncEnded(); } } + /// + /// Allows the specific request implementation to re-throw an exception if + /// the background process finished unsuccessfully + /// + protected override void ReraiseExceptions() { + if(this.exception != null) + throw this.exception; + } + /// Whether to use the ThreadPool for obtaining a background thread private bool useThreadPool; + /// Exception that has occured in the background process + private volatile Exception exception; } diff --git a/Source/Tracking/Request.cs b/Source/Tracking/Request.cs index 154eb4d..25d9d7d 100644 --- a/Source/Tracking/Request.cs +++ b/Source/Tracking/Request.cs @@ -37,7 +37,7 @@ namespace Nuclex.Support.Tracking { /// OnAsyncEnded(), no matter what the outcome of your background operation is. /// /// - public class Request : Progression { + public abstract class Request : Progression { #region class EndedDummyRequest @@ -48,21 +48,25 @@ namespace Nuclex.Support.Tracking { /// Creates a new failed dummy request /// Exception that caused the dummy to fail public EndedDummyRequest(Exception exception) { + this.exception = exception; OnAsyncEnded(); - - // Only call SetException() if an actual exception was provided. Who knows what - // evil code might be inside SetException() after all ;) - if(exception != null) - SetException(exception); } + /// + /// Allows the specific request implementation to re-throw an exception if + /// the background process finished unsuccessfully + /// + protected override void ReraiseExceptions() { + if(this.exception != null) + throw this.exception; + } + /// Exception that supposedly caused the request to fail + private Exception exception; } #endregion // EndedDummyRequest /// Creates a new failed dummy request - /// - /// Exception that supposedly caused the progression to fail - /// + /// Exception that supposedly caused the request to fail /// /// A failed request that reports the provided exception as cause for its failure /// @@ -83,36 +87,76 @@ namespace Nuclex.Support.Tracking { if(!Ended) WaitHandle.WaitOne(); - // If an exception occured during the background execution - if(this.occuredException != null) - throw this.occuredException; + // Allow the implementor to throw an exception in case an error has occured + ReraiseExceptions(); } - /// Exception that occured while the operation was executing + /// + /// Allows the specific request implementation to re-throw an exception if + /// the background process finished unsuccessfully + /// + protected virtual void ReraiseExceptions() {} + + } + + /// Request providing a result that can be passed to the caller + /// + /// Type of the result being provided by the request + /// + public abstract class Request : Request { + + #region class EndedDummyRequest + + /// Dummy request that is always in the ended state + private class EndedDummyRequest : Request { + /// Creates a new failed dummy request + /// Exception that caused the dummy to fail + public EndedDummyRequest(Exception exception) { + this.exception = exception; + OnAsyncEnded(); + } + /// + /// Allows the specific request implementation to re-throw an exception if + /// the background process finished unsuccessfully + /// + protected override ResultType GatherResults() { + throw this.exception; + } + /// Exception that supposedly caused the request to fail + private Exception exception; + } + + #endregion // EndedDummyRequest + + /// Creates a new failed dummy request + /// Exception that supposedly caused the request to fail + /// + /// A failed request that reports the provided exception as cause for its failure + /// + public static new Request CreateFailedDummyRequest(Exception exception) { + return new EndedDummyRequest(exception); + } + + /// Waits for the background operation to end /// - /// If this field is null, it is assumed that no exception has occured - /// in the background process. If it is set, however, the End() method will - /// re-raise the exception to the calling thread when it is called. + /// Any exceptions raised in the background operation will be thrown + /// in this method. If you decide to override this method, you should + /// call End() first (and let any possible exception through to your + /// caller). /// - public Exception OccuredException { - get { return this.occuredException; } + public new ResultType Join() { + base.Join(); + + // Return the results of the request + return GatherResults(); } - /// Sets the exception to raise to the caller of the End() method - /// Exception to raise to the caller of the End() method - protected void SetException(Exception exception) { - - // We allow the caller to set the exception multiple times. While I certainly - // can't think of a scenario where this would happen, throwing an exception - // in that case seems worse. The caller might just be executing an exception - // handling block and locking + throwing here could cause all kinds of problems. - this.occuredException = exception; - - } - - /// Exception that occured while the operation was executing - private volatile Exception occuredException; + /// + /// Allows the specific request implementation to re-throw an exception if + /// the background process finished unsuccessfully + /// + protected abstract ResultType GatherResults(); }