#region CPL License /* Nuclex Framework Copyright (C) 2002-2008 Nuclex Development Labs This library is free software; you can redistribute it and/or modify it under the terms of the IBM Common Public License as published by the IBM Corporation; either version 1.0 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the IBM Common Public License for more details. You should have received a copy of the IBM Common Public License along with this library */ #endregion using System; using System.Collections.Generic; namespace Nuclex.Support.Tracking { /// Extended type of progression that is able to fail /// /// /// If the background process fails, the exception that caused it to fail is /// communicated to all parties waiting on the progression through the /// Exception property. Implementers should place their code in try..catch /// blocks and call SetException() to temporarily store the exception for /// retrieval by the caller(s). /// /// /// As with all progressions, the interface contract still requires you to call /// OnAsyncEnded(), no matter what the outcome of your background operation is. /// /// public class Request : Progression { #region class EndedDummyRequest /// Dummy request that is always in the ended state private class EndedDummyRequest : Request { /// Creates a new successfully completed dummy request public EndedDummyRequest() : this(null) { } /// Creates a new failed dummy request /// Exception that caused the dummy to fail public EndedDummyRequest(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); } } #endregion // EndedDummyRequest /// Creates a new failed dummy request /// /// Exception that supposedly caused the progression to fail /// /// /// A failed request that reports the provided exception as cause for its failure /// public static Request CreateFailedDummyRequest(Exception exception) { return new EndedDummyRequest(exception); } /// Waits for the background operation to end /// /// 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 virtual void Join() { // If the progression itself hasn't ended yet, block the caller until it has. if(!Ended) WaitHandle.WaitOne(); // If an exception occured during the background execution if(this.occuredException != null) throw this.occuredException; } /// Exception that occured while the operation was executing /// /// 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. /// public Exception OccuredException { get { return this.occuredException; } } /// 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; } } // namespace Nuclex.Support.Tracking