//---------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. //---------------------------------------------------------------- namespace System.ServiceModel.Discovery { using System.Collections.Generic; using System.Runtime; using System.Threading; abstract class IteratorAsyncResult : AsyncResult { TIteratorState iterState; TimeoutHelper timeoutHelper; IEnumerator steps; bool completedSynchronously; int completedCalled; int numPendingSteps; bool shouldComplete; object thisLock; AsyncCallback onStepCompletedCallback; protected IteratorAsyncResult(AsyncCallback callback, object state) : base(callback, state) { this.onStepCompletedCallback = Fx.ThunkCallback(new AsyncCallback(this.OnStepCompleted)); this.thisLock = new object(); } protected TimeSpan OriginalTimeout { get { return this.timeoutHelper.OriginalTimeout; } } public static AsyncStep CallAsync(BeginCall begin, EndCall end) { return new AsyncStep(begin, end, false); } public static AsyncStep CallAsync(BeginCall begin, EndCall end, IAsyncCatch[] catches) { return new AsyncStep(begin, end, false, catches); } public static AsyncStep CallParallel(BeginCall begin, EndCall end) { return new AsyncStep(begin, end, true); } public static AsyncStep CallParallel(BeginCall begin, EndCall end, IAsyncCatch[] catches) { return new AsyncStep(begin, end, true, catches); } protected void Start(TIteratorState iterState, TimeSpan timeout) { this.iterState = iterState; this.timeoutHelper = new TimeoutHelper(timeout); this.completedSynchronously = true; this.steps = this.GetAsyncSteps(); this.ExecuteSteps(); } protected TimeSpan RemainingTime() { return this.timeoutHelper.RemainingTime(); } protected abstract IEnumerator GetAsyncSteps(); protected void CompleteOnce() { this.CompleteOnce(null); } protected void CompleteOnce(Exception error) { if (Interlocked.CompareExchange(ref this.completedCalled, 1, 0) == 0) { base.Complete(this.completedSynchronously, error); } } void ExecuteSteps() { IAsyncResult result; AsyncStep currentStep; while (!this.IsCompleted) { if (!this.steps.MoveNext()) { this.CompleteIfNoPendingSteps(); break; } else { currentStep = this.steps.Current; result = this.StartStep(currentStep); if (result != null) { if (result.CompletedSynchronously) { this.FinishStep(currentStep, result); } else if (!currentStep.IsParallel) { break; } } } } } IAsyncResult StartStep(AsyncStep step) { IAsyncResult result = null; Exception error = null; try { this.OnStepStart(); result = step.Begin( this.iterState, this.timeoutHelper.RemainingTime(), this.onStepCompletedCallback, step); } catch (Exception e) { if (Fx.IsFatal(e)) { throw; } error = e; } if (error != null) { this.HandleException(error, step); } return result; } void OnStepStart() { lock (this.thisLock) { this.numPendingSteps++; } } void OnStepCompletion() { bool doComplete = false; lock (this.thisLock) { this.numPendingSteps--; if ((this.numPendingSteps == 0) && this.shouldComplete) { doComplete = true; } } if (doComplete) { this.CompleteOnce(); } } void CompleteIfNoPendingSteps() { bool doComplete = false; lock (this.thisLock) { if (this.numPendingSteps == 0) { doComplete = true; } else { this.shouldComplete = true; } } if (doComplete) { this.CompleteOnce(); } } void OnStepCompleted(IAsyncResult result) { if (result.CompletedSynchronously) { return; } this.completedSynchronously = false; AsyncStep step = (AsyncStep)result.AsyncState; this.FinishStep(step, result); if (!step.IsParallel) { this.ExecuteSteps(); } } void FinishStep(AsyncStep step, IAsyncResult result) { Exception error = null; try { step.End(this.iterState, result); } catch (Exception e) { if (Fx.IsFatal(e)) { throw; } error = e; } if (error != null) { this.HandleException(error, step); } this.OnStepCompletion(); } void HandleException(Exception e, AsyncStep step) { if (step.Catches != null) { Exception outException; for (int i = 0; i < step.Catches.Length; i++) { if (step.Catches[i].HandleException(iterState, e, out outException)) { if (outException != null) { this.CompleteOnce(outException); } else { return; } } } } // The exception wasn't handled this.CompleteOnce(e); } public delegate IAsyncResult BeginCall( TIteratorState iterState, TimeSpan timeout, AsyncCallback asyncCallback, object state); public delegate void EndCall(TIteratorState iterState, IAsyncResult result); public delegate Exception ExceptionHandler(TIteratorState iterState, TException exception) where TException : Exception; public class AsyncStep { public AsyncStep(BeginCall begin, EndCall end, bool isParallel) { this.Begin = begin; this.End = end; this.IsParallel = isParallel; } public AsyncStep(BeginCall begin, EndCall end, bool isParallel, IAsyncCatch[] catches) : this(begin, end, isParallel) { this.Catches = catches; } public IAsyncCatch[] Catches { get; private set; } public BeginCall Begin { get; private set; } public EndCall End { get; private set; } public bool IsParallel { get; private set; } } public interface IAsyncCatch { bool HandleException(TIteratorState iterState, Exception ex, out Exception outEx); } public class AsyncCatch : IAsyncCatch where TException : Exception { readonly ExceptionHandler handler; public AsyncCatch(ExceptionHandler handler) { this.handler = handler; } public bool HandleException(TIteratorState state, Exception ex, out Exception outEx) { outEx = null; TException casted = ex as TException; if (casted != null) { outEx = this.handler(state, casted); return true; } else { // The exception wasn't matched, try next handler return false; } } } } }