#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 { /// Asynchronous request running in the background /// /// /// If the background process fails, the exception that caused it to fail is /// communicated to all parties waiting on the Request through the Join() /// method. Implementers should store any errors occuring in the asynchronous /// parts of their code in a try..catch block (or avoid throwing and just /// store a new exception) and re-throw them when in ReraiseExceptions() /// /// /// Like in the Waitable class, the contract requires you to always call /// OnAsyncEnded(), no matter what the outcome of your operation is. /// /// public abstract class Request : Waitable { #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) { this.exception = exception; 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; } /// Exception that supposedly caused the request to fail private Exception exception; } #endregion // EndedDummyRequest /// Succeeded dummy request /// /// Use to indicate success if the request has already been completed at /// the time you are asked to perform it. /// public static readonly Request SucceededDummy = new 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 Request CreateFailedDummy(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 request itself hasn't ended yet, block the caller until it has. // We could just use WaitHandle.WaitOne() here, but since the WaitHandle is created // on-the-fly only when it is requested, we can avoid the WaitHandle creation in // case the request is already finished! if(!Ended) Wait(); // Allow the implementor to throw an exception in case an error has occured ReraiseExceptions(); } /// /// 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 SucceededDummyRequest /// Succeeded dummy request that is always in the ended state private class SucceededDummyRequest : Request { /// Creates a new failed dummy request /// Result to return to the request's caller public SucceededDummyRequest(ResultType result) { this.result = result; OnAsyncEnded(); } /// /// Allows the specific request implementation to re-throw an exception if /// the background process finished unsuccessfully /// protected override ResultType GatherResults() { return this.result; } /// Results the succeede dummy request will provide to the caller private ResultType result; } #endregion // SucceededDummyRequest #region class FailedDummyRequest /// Failed dummy request that is always in the ended state private class FailedDummyRequest : Request { /// Creates a new failed dummy request /// Exception that caused the dummy to fail public FailedDummyRequest(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 // FailedDummyRequest /// Creates a new failed dummy request /// Result to provide to the caller /// /// A succeeded request that returns the provided result to the caller /// public static Request CreateSucceededDummy(ResultType result) { return new SucceededDummyRequest(result); } /// 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 CreateFailedDummy(Exception exception) { return new FailedDummyRequest(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 new ResultType Join() { base.Join(); // Return the results of the request return GatherResults(); } /// /// Allows the specific request implementation to re-throw an exception if /// the background process finished unsuccessfully /// protected abstract ResultType GatherResults(); } } // namespace Nuclex.Support.Tracking