|
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------
namespace System.ServiceModel.Channels
{
using System;
using System.Diagnostics;
using System.Runtime;
using System.ServiceModel;
using System.ServiceModel.Diagnostics;
using System.ServiceModel.Diagnostics.Application;
using System.Transactions;
using System.Runtime.Diagnostics;
public abstract class ReceiveContext
{
public readonly static string Name = "ReceiveContext";
ThreadNeutralSemaphore stateLock; // protects state that may be reverted
bool contextFaulted;
object thisLock;
EventTraceActivity eventTraceActivity;
protected ReceiveContext()
{
this.thisLock = new object();
this.State = ReceiveContextState.Received;
this.stateLock = new ThreadNeutralSemaphore(1);
}
public ReceiveContextState State
{
get;
protected set;
}
protected object ThisLock
{
get { return thisLock; }
}
public event EventHandler Faulted;
public static bool TryGet(Message message, out ReceiveContext property)
{
if (message == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("message");
}
bool result = TryGet(message.Properties, out property);
if (result && FxTrace.Trace.IsEnd2EndActivityTracingEnabled && property.eventTraceActivity == null)
{
property.eventTraceActivity = EventTraceActivityHelper.TryExtractActivity(message);
}
return result;
}
public static bool TryGet(MessageProperties properties, out ReceiveContext property)
{
if (properties == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("properties");
}
property = null;
object foundProperty;
if (properties.TryGetValue(Name, out foundProperty))
{
property = (ReceiveContext)foundProperty;
return true;
}
return false;
}
public virtual void Abandon(TimeSpan timeout)
{
Abandon(null, timeout);
}
public virtual void Abandon(Exception exception, TimeSpan timeout)
{
EnsureValidTimeout(timeout);
TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
this.WaitForStateLock(timeoutHelper.RemainingTime());
try
{
if (PreAbandon())
{
return;
}
}
finally
{
// Abandon can never be reverted, release the state lock.
this.ReleaseStateLock();
}
bool success = false;
try
{
if (exception == null)
{
OnAbandon(timeoutHelper.RemainingTime());
}
else
{
if (TD.ReceiveContextAbandonWithExceptionIsEnabled())
{
TD.ReceiveContextAbandonWithException(this.eventTraceActivity, this.GetType().ToString(), exception.GetType().ToString());
}
OnAbandon(exception, timeoutHelper.RemainingTime());
}
lock (ThisLock)
{
ThrowIfFaulted();
ThrowIfNotAbandoning();
this.State = ReceiveContextState.Abandoned;
}
success = true;
}
finally
{
if (!success)
{
if (TD.ReceiveContextAbandonFailedIsEnabled())
{
TD.ReceiveContextAbandonFailed(this.eventTraceActivity, this.GetType().ToString());
}
Fault();
}
}
}
public virtual IAsyncResult BeginAbandon(TimeSpan timeout, AsyncCallback callback, object state)
{
return BeginAbandon(null, timeout, callback, state);
}
public virtual IAsyncResult BeginAbandon(Exception exception, TimeSpan timeout, AsyncCallback callback, object state)
{
EnsureValidTimeout(timeout);
return new AbandonAsyncResult(this, exception, timeout, callback, state);
}
public virtual IAsyncResult BeginComplete(TimeSpan timeout, AsyncCallback callback, object state)
{
EnsureValidTimeout(timeout);
return new CompleteAsyncResult(this, timeout, callback, state);
}
public virtual void Complete(TimeSpan timeout)
{
EnsureValidTimeout(timeout);
TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
this.WaitForStateLock(timeoutHelper.RemainingTime());
bool success = false;
try
{
PreComplete();
success = true;
}
finally
{
// Case 1: State validation fails, release the lock.
// Case 2: No trasaction, the state can never be reverted, release the lock.
// Case 3: Transaction, keep the lock until we know the transaction outcome (OnTransactionStatusNotification).
if (!success || Transaction.Current == null)
{
this.ReleaseStateLock();
}
}
success = false;
try
{
OnComplete(timeoutHelper.RemainingTime());
lock (ThisLock)
{
ThrowIfFaulted();
ThrowIfNotCompleting();
this.State = ReceiveContextState.Completed;
}
success = true;
}
finally
{
if (!success)
{
if (TD.ReceiveContextCompleteFailedIsEnabled())
{
TD.ReceiveContextCompleteFailed(this.eventTraceActivity, this.GetType().ToString());
}
Fault();
}
}
}
public virtual void EndAbandon(IAsyncResult result)
{
AbandonAsyncResult.End(result);
}
public virtual void EndComplete(IAsyncResult result)
{
CompleteAsyncResult.End(result);
}
void EnsureValidTimeout(TimeSpan timeout)
{
if (timeout < TimeSpan.Zero)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
new ArgumentOutOfRangeException("timeout", SR.GetString(SR.SFxTimeoutOutOfRange0)));
}
if (TimeoutHelper.IsTooLarge(timeout))
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
new ArgumentOutOfRangeException("timeout", timeout, SR.GetString(SR.SFxTimeoutOutOfRangeTooBig)));
}
}
protected internal virtual void Fault()
{
lock (ThisLock)
{
if (this.State == ReceiveContextState.Completed || this.State == ReceiveContextState.Abandoned || this.State == ReceiveContextState.Faulted)
{
return;
}
this.State = ReceiveContextState.Faulted;
}
OnFaulted();
}
protected abstract void OnAbandon(TimeSpan timeout);
protected virtual void OnAbandon(Exception exception, TimeSpan timeout)
{
// default implementation: delegate to non-exception overload, ignoring reason
OnAbandon(timeout);
}
protected abstract IAsyncResult OnBeginAbandon(TimeSpan timeout, AsyncCallback callback, object state);
protected virtual IAsyncResult OnBeginAbandon(Exception exception, TimeSpan timeout, AsyncCallback callback, object state)
{
// default implementation: delegate to non-exception overload, ignoring reason
return OnBeginAbandon(timeout, callback, state);
}
protected abstract IAsyncResult OnBeginComplete(TimeSpan timeout, AsyncCallback callback, object state);
protected abstract void OnComplete(TimeSpan timeout);
protected abstract void OnEndAbandon(IAsyncResult result);
protected abstract void OnEndComplete(IAsyncResult result);
protected virtual void OnFaulted()
{
lock (ThisLock)
{
if (this.contextFaulted)
{
return;
}
this.contextFaulted = true;
}
if (TD.ReceiveContextFaultedIsEnabled())
{
TD.ReceiveContextFaulted(this.eventTraceActivity, this);
}
EventHandler handler = this.Faulted;
if (handler != null)
{
try
{
handler(this, EventArgs.Empty);
}
catch (Exception exception)
{
if (Fx.IsFatal(exception))
{
throw;
}
throw DiagnosticUtility.ExceptionUtility.ThrowHelperCallback(exception);
}
}
}
void OnTransactionStatusNotification(TransactionStatus status)
{
lock (ThisLock)
{
if (status == TransactionStatus.Aborted)
{
if (this.State == ReceiveContextState.Completing || this.State == ReceiveContextState.Completed)
{
this.State = ReceiveContextState.Received;
}
}
}
if (status != TransactionStatus.Active)
{
this.ReleaseStateLock();
}
}
bool PreAbandon()
{
bool alreadyAbandoned = false;
lock (ThisLock)
{
if (this.State == ReceiveContextState.Abandoning || this.State == ReceiveContextState.Abandoned)
{
alreadyAbandoned = true;
}
else
{
ThrowIfFaulted();
ThrowIfNotReceived();
this.State = ReceiveContextState.Abandoning;
}
}
return alreadyAbandoned;
}
void PreComplete()
{
lock (ThisLock)
{
ThrowIfFaulted();
ThrowIfNotReceived();
if (Transaction.Current != null)
{
Transaction.Current.EnlistVolatile(new EnlistmentNotifications(this), EnlistmentOptions.None);
}
this.State = ReceiveContextState.Completing;
}
}
void ReleaseStateLock()
{
this.stateLock.Exit();
}
void ThrowIfFaulted()
{
if (State == ReceiveContextState.Faulted)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
new CommunicationException(SR.GetString(SR.ReceiveContextFaulted, this.GetType().ToString())));
}
}
void ThrowIfNotAbandoning()
{
if (State != ReceiveContextState.Abandoning)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
new InvalidOperationException(SR.GetString(SR.ReceiveContextInInvalidState, this.GetType().ToString(), this.State.ToString())));
}
}
void ThrowIfNotCompleting()
{
if (State != ReceiveContextState.Completing)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
new InvalidOperationException(SR.GetString(SR.ReceiveContextInInvalidState, this.GetType().ToString(), this.State.ToString())));
}
}
void ThrowIfNotReceived()
{
if (State != ReceiveContextState.Received)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
new InvalidOperationException(SR.GetString(SR.ReceiveContextCannotBeUsed, this.GetType().ToString(), this.State.ToString())));
}
}
void WaitForStateLock(TimeSpan timeout)
{
try
{
this.stateLock.Enter(timeout);
}
catch (TimeoutException exception)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(WrapStateException(exception));
}
}
bool WaitForStateLockAsync(TimeSpan timeout, FastAsyncCallback callback, object state)
{
return this.stateLock.EnterAsync(timeout, callback, state);
}
Exception WrapStateException(Exception exception)
{
return new InvalidOperationException(SR.GetString(SR.ReceiveContextInInvalidState, this.GetType().ToString(), this.State.ToString()), exception);
}
sealed class AbandonAsyncResult : WaitAndContinueOperationAsyncResult
{
Exception exception;
static AsyncCompletion handleOperationComplete = new AsyncCompletion(HandleOperationComplete);
public AbandonAsyncResult(ReceiveContext receiveContext, Exception exception, TimeSpan timeout, AsyncCallback callback, object state)
: base(receiveContext, timeout, callback, state)
{
this.exception = exception;
base.Begin();
}
// The main Abandon logic.
protected override bool ContinueOperation()
{
try
{
if (this.ReceiveContext.PreAbandon())
{
return true;
}
}
finally
{
// Abandon can never be reverted, release the state lock.
this.ReceiveContext.ReleaseStateLock();
}
bool success = false;
IAsyncResult result;
try
{
if (exception == null)
{
result = this.ReceiveContext.OnBeginAbandon(this.TimeoutHelper.RemainingTime(), PrepareAsyncCompletion(handleOperationComplete), this);
}
else
{
if (TD.ReceiveContextAbandonWithExceptionIsEnabled())
{
TD.ReceiveContextAbandonWithException(this.ReceiveContext.eventTraceActivity, this.GetType().ToString(), exception.GetType().ToString());
}
result = this.ReceiveContext.OnBeginAbandon(exception, this.TimeoutHelper.RemainingTime(), PrepareAsyncCompletion(handleOperationComplete), this);
}
success = true;
}
finally
{
if (!success)
{
if (TD.ReceiveContextAbandonFailedIsEnabled())
{
TD.ReceiveContextAbandonFailed((this.ReceiveContext != null) ? this.ReceiveContext.eventTraceActivity : null,
this.GetType().ToString());
}
this.ReceiveContext.Fault();
}
}
return SyncContinue(result);
}
public static void End(IAsyncResult result)
{
AsyncResult.End<AbandonAsyncResult>(result);
}
void EndAbandon(IAsyncResult result)
{
this.ReceiveContext.OnEndAbandon(result);
lock (this.ReceiveContext.ThisLock)
{
this.ReceiveContext.ThrowIfFaulted();
this.ReceiveContext.ThrowIfNotAbandoning();
this.ReceiveContext.State = ReceiveContextState.Abandoned;
}
}
static bool HandleOperationComplete(IAsyncResult result)
{
bool success = false;
AbandonAsyncResult thisPtr = (AbandonAsyncResult)result.AsyncState;
try
{
thisPtr.EndAbandon(result);
success = true;
return true;
}
finally
{
if (!success)
{
if (TD.ReceiveContextAbandonFailedIsEnabled())
{
TD.ReceiveContextAbandonFailed(thisPtr.ReceiveContext.eventTraceActivity, thisPtr.GetType().ToString());
}
thisPtr.ReceiveContext.Fault();
}
}
}
}
sealed class CompleteAsyncResult : WaitAndContinueOperationAsyncResult
{
Transaction transaction;
static AsyncCompletion handleOperationComplete = new AsyncCompletion(HandleOperationComplete);
public CompleteAsyncResult(ReceiveContext receiveContext, TimeSpan timeout, AsyncCallback callback, object state)
: base(receiveContext, timeout, callback, state)
{
this.transaction = Transaction.Current;
this.Begin();
}
protected override bool ContinueOperation()
{
IAsyncResult result;
using (PrepareTransactionalCall(this.transaction))
{
bool success = false;
try
{
this.ReceiveContext.PreComplete();
success = true;
}
finally
{
// Case 1: State validation fails, release the lock.
// Case 2: No trasaction, the state can never be reverted, release the lock.
// Case 3: Transaction, keep the lock until we know the transaction outcome (OnTransactionStatusNotification).
if (!success || this.transaction == null)
{
this.ReceiveContext.ReleaseStateLock();
}
}
success = false;
try
{
result = this.ReceiveContext.OnBeginComplete(this.TimeoutHelper.RemainingTime(), PrepareAsyncCompletion(handleOperationComplete), this);
success = true;
}
finally
{
if (!success)
{
if (TD.ReceiveContextCompleteFailedIsEnabled())
{
TD.ReceiveContextCompleteFailed(this.ReceiveContext.eventTraceActivity, this.GetType().ToString());
}
this.ReceiveContext.Fault();
}
}
}
return SyncContinue(result);
}
public static void End(IAsyncResult result)
{
AsyncResult.End<CompleteAsyncResult>(result);
}
void EndComplete(IAsyncResult result)
{
this.ReceiveContext.OnEndComplete(result);
lock (this.ReceiveContext.ThisLock)
{
this.ReceiveContext.ThrowIfFaulted();
this.ReceiveContext.ThrowIfNotCompleting();
this.ReceiveContext.State = ReceiveContextState.Completed;
}
}
static bool HandleOperationComplete(IAsyncResult result)
{
CompleteAsyncResult thisPtr = (CompleteAsyncResult)result.AsyncState;
bool success = false;
try
{
thisPtr.EndComplete(result);
success = true;
return true;
}
finally
{
if (!success)
{
if (TD.ReceiveContextCompleteFailedIsEnabled())
{
TD.ReceiveContextCompleteFailed(thisPtr.ReceiveContext.eventTraceActivity, thisPtr.GetType().ToString());
}
thisPtr.ReceiveContext.Fault();
}
}
}
}
class EnlistmentNotifications : IEnlistmentNotification
{
ReceiveContext context;
public EnlistmentNotifications(ReceiveContext context)
{
this.context = context;
}
public void Commit(Enlistment enlistment)
{
this.context.OnTransactionStatusNotification(TransactionStatus.Committed);
enlistment.Done();
}
public void InDoubt(Enlistment enlistment)
{
this.context.OnTransactionStatusNotification(TransactionStatus.InDoubt);
enlistment.Done();
}
public void Prepare(PreparingEnlistment preparingEnlistment)
{
this.context.OnTransactionStatusNotification(TransactionStatus.Active);
preparingEnlistment.Prepared();
}
public void Rollback(Enlistment enlistment)
{
this.context.OnTransactionStatusNotification(TransactionStatus.Aborted);
enlistment.Done();
}
}
abstract class WaitAndContinueOperationAsyncResult : TransactedAsyncResult
{
static FastAsyncCallback onWaitForStateLockComplete = new FastAsyncCallback(OnWaitForStateLockComplete);
public WaitAndContinueOperationAsyncResult(ReceiveContext receiveContext, TimeSpan timeout, AsyncCallback callback, object state)
: base(callback, state)
{
this.ReceiveContext = receiveContext;
this.TimeoutHelper = new TimeoutHelper(timeout);
}
protected ReceiveContext ReceiveContext
{
get;
private set;
}
protected TimeoutHelper TimeoutHelper
{
get;
private set;
}
protected void Begin()
{
if (!this.ReceiveContext.WaitForStateLockAsync(this.TimeoutHelper.RemainingTime(), onWaitForStateLockComplete, this))
{
return;
}
if (this.ContinueOperation())
{
this.Complete(true);
}
}
protected abstract bool ContinueOperation();
static void OnWaitForStateLockComplete(object state, Exception asyncException)
{
WaitAndContinueOperationAsyncResult thisPtr = (WaitAndContinueOperationAsyncResult)state;
bool completeAsyncResult = true;
Exception completeException = null;
if (asyncException != null)
{
if (asyncException is TimeoutException)
{
asyncException = thisPtr.ReceiveContext.WrapStateException(asyncException);
}
completeException = asyncException;
}
else
{
try
{
completeAsyncResult = thisPtr.ContinueOperation();
}
catch (Exception e)
{
if (Fx.IsFatal(e))
{
throw;
}
completeException = e;
}
}
if (completeAsyncResult)
{
thisPtr.Complete(false, completeException);
}
}
}
}
}
|