334 lines
9.1 KiB
C#
334 lines
9.1 KiB
C#
|
//----------------------------------------------------------------
|
||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
//----------------------------------------------------------------
|
||
|
|
||
|
namespace System.ServiceModel.Discovery
|
||
|
{
|
||
|
using System.Collections.Generic;
|
||
|
using System.Runtime;
|
||
|
using System.Threading;
|
||
|
|
||
|
abstract class IteratorAsyncResult<TIteratorState> : AsyncResult
|
||
|
{
|
||
|
TIteratorState iterState;
|
||
|
TimeoutHelper timeoutHelper;
|
||
|
IEnumerator<AsyncStep> 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<AsyncStep> 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<TException>(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<TException> : IAsyncCatch
|
||
|
where TException : Exception
|
||
|
{
|
||
|
readonly ExceptionHandler<TException> handler;
|
||
|
|
||
|
public AsyncCatch(ExceptionHandler<TException> 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;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|