|
//-----------------------------------------------------------------------------
// <copyright file="TransactionState.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------------
namespace System.Transactions
{
using System;
using System.Collections;
using System.Diagnostics;
using System.Globalization;
using System.Runtime.Serialization;
using System.Threading;
using System.Transactions.Diagnostics;
// The TransactionState object defines the basic set of operations that
// are available for a transaction. It is a base type and the base
// implementations all throw exceptions. For a particular state a derived
// implementation will inheret from this object and implement the appropriate
// operations for that state.
internal abstract class TransactionState
{
// The state machines themselves are designed to be internally consistent. So the only externally visable
// state transition is to active. All other state transitions must happen within the state machines
// themselves.
// Double-checked locking pattern requires volatile for read/write synchronization
private static volatile TransactionStateActive _transactionStateActive;
private static volatile TransactionStateSubordinateActive _transactionStateSubordinateActive;
private static volatile TransactionStatePhase0 _transactionStatePhase0;
private static volatile TransactionStateVolatilePhase1 _transactionStateVolatilePhase1;
private static volatile TransactionStateVolatileSPC _transactionStateVolatileSPC;
private static volatile TransactionStateSPC _transactionStateSPC;
private static volatile TransactionStateAborted _transactionStateAborted;
private static volatile TransactionStateCommitted _transactionStateCommitted;
private static volatile TransactionStateInDoubt _transactionStateInDoubt;
private static volatile TransactionStatePromoted _transactionStatePromoted;
private static volatile TransactionStateNonCommittablePromoted _transactionStateNonCommittablePromoted;
private static volatile TransactionStatePromotedP0Wave _transactionStatePromotedP0Wave;
private static volatile TransactionStatePromotedCommitting _transactionStatePromotedCommitting;
private static volatile TransactionStatePromotedPhase0 _transactionStatePromotedPhase0;
private static volatile TransactionStatePromotedPhase1 _transactionStatePromotedPhase1;
private static volatile TransactionStatePromotedP0Aborting _transactionStatePromotedP0Aborting;
private static volatile TransactionStatePromotedP1Aborting _transactionStatePromotedP1Aborting;
private static volatile TransactionStatePromotedAborted _transactionStatePromotedAborted;
private static volatile TransactionStatePromotedCommitted _transactionStatePromotedCommitted;
private static volatile TransactionStatePromotedIndoubt _transactionStatePromotedIndoubt;
private static volatile TransactionStateDelegated _transactionStateDelegated;
private static volatile TransactionStateDelegatedSubordinate _transactionStateDelegatedSubordinate;
private static volatile TransactionStateDelegatedP0Wave _transactionStateDelegatedP0Wave;
private static volatile TransactionStateDelegatedCommitting _transactionStateDelegatedCommitting;
private static volatile TransactionStateDelegatedAborting _transactionStateDelegatedAborting;
private static volatile TransactionStatePSPEOperation _transactionStatePSPEOperation;
private static volatile TransactionStateDelegatedNonMSDTC _transactionStateDelegatedNonMSDTC;
private static volatile TransactionStatePromotedNonMSDTCPhase0 _transactionStatePromotedNonMSDTCPhase0;
private static volatile TransactionStatePromotedNonMSDTCVolatilePhase1 _transactionStatePromotedNonMSDTCVolatilePhase1;
private static volatile TransactionStatePromotedNonMSDTCSinglePhaseCommit _transactionStatePromotedNonMSDTCSinglePhaseCommit;
private static volatile TransactionStatePromotedNonMSDTCAborted _transactionStatePromotedNonMSDTCAborted;
private static volatile TransactionStatePromotedNonMSDTCCommitted _transactionStatePromotedNonMSDTCCommitted;
private static volatile TransactionStatePromotedNonMSDTCIndoubt _transactionStatePromotedNonMSDTCIndoubt;
// Object for synchronizing access to the entire class( avoiding lock( typeof( ... )) )
private static object classSyncObject;
internal static TransactionStateActive _TransactionStateActive
{
get
{
if (_transactionStateActive == null)
{
lock (ClassSyncObject)
{
if (_transactionStateActive == null)
{
TransactionStateActive temp = new TransactionStateActive();
_transactionStateActive = temp;
}
}
}
return _transactionStateActive;
}
}
internal static TransactionStateSubordinateActive _TransactionStateSubordinateActive
{
get
{
if (_transactionStateSubordinateActive == null)
{
lock (ClassSyncObject)
{
if (_transactionStateSubordinateActive == null)
{
TransactionStateSubordinateActive temp = new TransactionStateSubordinateActive();
_transactionStateSubordinateActive = temp;
}
}
}
return _transactionStateSubordinateActive;
}
}
internal static TransactionStatePSPEOperation _TransactionStatePSPEOperation
{
get
{
if (_transactionStatePSPEOperation == null)
{
lock (ClassSyncObject)
{
if (_transactionStatePSPEOperation == null)
{
TransactionStatePSPEOperation temp = new TransactionStatePSPEOperation();
_transactionStatePSPEOperation = temp;
}
}
}
return _transactionStatePSPEOperation;
}
}
protected static TransactionStatePhase0 _TransactionStatePhase0
{
get
{
if (_transactionStatePhase0 == null)
{
lock (ClassSyncObject)
{
if (_transactionStatePhase0 == null)
{
TransactionStatePhase0 temp = new TransactionStatePhase0();
_transactionStatePhase0 = temp;
}
}
}
return _transactionStatePhase0;
}
}
protected static TransactionStateVolatilePhase1 _TransactionStateVolatilePhase1
{
get
{
if (_transactionStateVolatilePhase1 == null)
{
lock (ClassSyncObject)
{
if (_transactionStateVolatilePhase1 == null)
{
TransactionStateVolatilePhase1 temp = new TransactionStateVolatilePhase1();
_transactionStateVolatilePhase1 = temp;
}
}
}
return _transactionStateVolatilePhase1;
}
}
protected static TransactionStateVolatileSPC _TransactionStateVolatileSPC
{
get
{
if (_transactionStateVolatileSPC == null)
{
lock (ClassSyncObject)
{
if (_transactionStateVolatileSPC == null)
{
TransactionStateVolatileSPC temp = new TransactionStateVolatileSPC();
_transactionStateVolatileSPC = temp;
}
}
}
return _transactionStateVolatileSPC;
}
}
protected static TransactionStateSPC _TransactionStateSPC
{
get
{
if (_transactionStateSPC == null)
{
lock (ClassSyncObject)
{
if (_transactionStateSPC == null)
{
TransactionStateSPC temp = new TransactionStateSPC();
_transactionStateSPC = temp;
}
}
}
return _transactionStateSPC;
}
}
protected static TransactionStateAborted _TransactionStateAborted
{
get
{
if (_transactionStateAborted == null)
{
lock (ClassSyncObject)
{
if (_transactionStateAborted == null)
{
TransactionStateAborted temp = new TransactionStateAborted();
_transactionStateAborted = temp;
}
}
}
return _transactionStateAborted;
}
}
protected static TransactionStateCommitted _TransactionStateCommitted
{
get
{
if (_transactionStateCommitted == null)
{
lock (ClassSyncObject)
{
if (_transactionStateCommitted == null)
{
TransactionStateCommitted temp = new TransactionStateCommitted();
_transactionStateCommitted = temp;
}
}
}
return _transactionStateCommitted;
}
}
protected static TransactionStateInDoubt _TransactionStateInDoubt
{
get
{
if (_transactionStateInDoubt == null)
{
lock (ClassSyncObject)
{
if (_transactionStateInDoubt == null)
{
TransactionStateInDoubt temp = new TransactionStateInDoubt();
_transactionStateInDoubt = temp;
}
}
}
return _transactionStateInDoubt;
}
}
internal static TransactionStatePromoted _TransactionStatePromoted
{
get
{
if (_transactionStatePromoted == null)
{
lock (ClassSyncObject)
{
if (_transactionStatePromoted == null)
{
TransactionStatePromoted temp = new TransactionStatePromoted();
_transactionStatePromoted = temp;
}
}
}
return _transactionStatePromoted;
}
}
internal static TransactionStateNonCommittablePromoted _TransactionStateNonCommittablePromoted
{
get
{
if (_transactionStateNonCommittablePromoted == null)
{
lock (ClassSyncObject)
{
if (_transactionStateNonCommittablePromoted == null)
{
TransactionStateNonCommittablePromoted temp = new TransactionStateNonCommittablePromoted();
_transactionStateNonCommittablePromoted = temp;
}
}
}
return _transactionStateNonCommittablePromoted;
}
}
protected static TransactionStatePromotedP0Wave _TransactionStatePromotedP0Wave
{
get
{
if (_transactionStatePromotedP0Wave == null)
{
lock (ClassSyncObject)
{
if (_transactionStatePromotedP0Wave == null)
{
TransactionStatePromotedP0Wave temp = new TransactionStatePromotedP0Wave();
_transactionStatePromotedP0Wave = temp;
}
}
}
return _transactionStatePromotedP0Wave;
}
}
protected static TransactionStatePromotedCommitting _TransactionStatePromotedCommitting
{
get
{
if (_transactionStatePromotedCommitting == null)
{
lock (ClassSyncObject)
{
if (_transactionStatePromotedCommitting == null)
{
TransactionStatePromotedCommitting temp = new TransactionStatePromotedCommitting();
_transactionStatePromotedCommitting = temp;
}
}
}
return _transactionStatePromotedCommitting;
}
}
protected static TransactionStatePromotedPhase0 _TransactionStatePromotedPhase0
{
get
{
if (_transactionStatePromotedPhase0 == null)
{
lock (ClassSyncObject)
{
if (_transactionStatePromotedPhase0 == null)
{
TransactionStatePromotedPhase0 temp = new TransactionStatePromotedPhase0();
_transactionStatePromotedPhase0 = temp;
}
}
}
return _transactionStatePromotedPhase0;
}
}
protected static TransactionStatePromotedPhase1 _TransactionStatePromotedPhase1
{
get
{
if (_transactionStatePromotedPhase1 == null)
{
lock (ClassSyncObject)
{
if (_transactionStatePromotedPhase1 == null)
{
TransactionStatePromotedPhase1 temp = new TransactionStatePromotedPhase1();
_transactionStatePromotedPhase1 = temp;
}
}
}
return _transactionStatePromotedPhase1;
}
}
protected static TransactionStatePromotedP0Aborting _TransactionStatePromotedP0Aborting
{
get
{
if (_transactionStatePromotedP0Aborting == null)
{
lock (ClassSyncObject)
{
if (_transactionStatePromotedP0Aborting == null)
{
TransactionStatePromotedP0Aborting temp = new TransactionStatePromotedP0Aborting();
_transactionStatePromotedP0Aborting = temp;
}
}
}
return _transactionStatePromotedP0Aborting;
}
}
protected static TransactionStatePromotedP1Aborting _TransactionStatePromotedP1Aborting
{
get
{
if (_transactionStatePromotedP1Aborting == null)
{
lock (ClassSyncObject)
{
if (_transactionStatePromotedP1Aborting == null)
{
TransactionStatePromotedP1Aborting temp = new TransactionStatePromotedP1Aborting();
_transactionStatePromotedP1Aborting = temp;
}
}
}
return _transactionStatePromotedP1Aborting;
}
}
protected static TransactionStatePromotedAborted _TransactionStatePromotedAborted
{
get
{
if (_transactionStatePromotedAborted == null)
{
lock (ClassSyncObject)
{
if (_transactionStatePromotedAborted == null)
{
TransactionStatePromotedAborted temp = new TransactionStatePromotedAborted();
_transactionStatePromotedAborted = temp;
}
}
}
return _transactionStatePromotedAborted;
}
}
protected static TransactionStatePromotedCommitted _TransactionStatePromotedCommitted
{
get
{
if (_transactionStatePromotedCommitted == null)
{
lock (ClassSyncObject)
{
if (_transactionStatePromotedCommitted == null)
{
TransactionStatePromotedCommitted temp = new TransactionStatePromotedCommitted();
_transactionStatePromotedCommitted = temp;
}
}
}
return _transactionStatePromotedCommitted;
}
}
protected static TransactionStatePromotedIndoubt _TransactionStatePromotedIndoubt
{
get
{
if (_transactionStatePromotedIndoubt == null)
{
lock (ClassSyncObject)
{
if (_transactionStatePromotedIndoubt == null)
{
TransactionStatePromotedIndoubt temp = new TransactionStatePromotedIndoubt();
_transactionStatePromotedIndoubt = temp;
}
}
}
return _transactionStatePromotedIndoubt;
}
}
protected static TransactionStateDelegated _TransactionStateDelegated
{
get
{
if (_transactionStateDelegated == null)
{
lock (ClassSyncObject)
{
if (_transactionStateDelegated == null)
{
TransactionStateDelegated temp = new TransactionStateDelegated();
_transactionStateDelegated = temp;
}
}
}
return _transactionStateDelegated;
}
}
internal static TransactionStateDelegatedSubordinate _TransactionStateDelegatedSubordinate
{
get
{
if (_transactionStateDelegatedSubordinate == null)
{
lock (ClassSyncObject)
{
if (_transactionStateDelegatedSubordinate == null)
{
TransactionStateDelegatedSubordinate temp = new TransactionStateDelegatedSubordinate();
_transactionStateDelegatedSubordinate = temp;
}
}
}
return _transactionStateDelegatedSubordinate;
}
}
protected static TransactionStateDelegatedP0Wave _TransactionStateDelegatedP0Wave
{
get
{
if (_transactionStateDelegatedP0Wave == null)
{
lock (ClassSyncObject)
{
if (_transactionStateDelegatedP0Wave == null)
{
TransactionStateDelegatedP0Wave temp = new TransactionStateDelegatedP0Wave();
_transactionStateDelegatedP0Wave = temp;
}
}
}
return _transactionStateDelegatedP0Wave;
}
}
protected static TransactionStateDelegatedCommitting _TransactionStateDelegatedCommitting
{
get
{
if (_transactionStateDelegatedCommitting == null)
{
lock (ClassSyncObject)
{
if (_transactionStateDelegatedCommitting == null)
{
TransactionStateDelegatedCommitting temp = new TransactionStateDelegatedCommitting();
_transactionStateDelegatedCommitting = temp;
}
}
}
return _transactionStateDelegatedCommitting;
}
}
protected static TransactionStateDelegatedAborting _TransactionStateDelegatedAborting
{
get
{
if (_transactionStateDelegatedAborting == null)
{
lock (ClassSyncObject)
{
if (_transactionStateDelegatedAborting == null)
{
TransactionStateDelegatedAborting temp = new TransactionStateDelegatedAborting();
_transactionStateDelegatedAborting = temp;
}
}
}
return _transactionStateDelegatedAborting;
}
}
protected static TransactionStateDelegatedNonMSDTC _TransactionStateDelegatedNonMSDTC
{
get
{
if (_transactionStateDelegatedNonMSDTC == null)
{
lock (ClassSyncObject)
{
if (_transactionStateDelegatedNonMSDTC == null)
{
TransactionStateDelegatedNonMSDTC temp = new TransactionStateDelegatedNonMSDTC();
_transactionStateDelegatedNonMSDTC = temp;
}
}
}
return _transactionStateDelegatedNonMSDTC;
}
}
protected static TransactionStatePromotedNonMSDTCPhase0 _TransactionStatePromotedNonMSDTCPhase0
{
get
{
if (_transactionStatePromotedNonMSDTCPhase0 == null)
{
lock (ClassSyncObject)
{
if (_transactionStatePromotedNonMSDTCPhase0 == null)
{
TransactionStatePromotedNonMSDTCPhase0 temp = new TransactionStatePromotedNonMSDTCPhase0();
_transactionStatePromotedNonMSDTCPhase0 = temp;
}
}
}
return _transactionStatePromotedNonMSDTCPhase0;
}
}
protected static TransactionStatePromotedNonMSDTCVolatilePhase1 _TransactionStatePromotedNonMSDTCVolatilePhase1
{
get
{
if (_transactionStatePromotedNonMSDTCVolatilePhase1 == null)
{
lock (ClassSyncObject)
{
if (_transactionStatePromotedNonMSDTCVolatilePhase1 == null)
{
TransactionStatePromotedNonMSDTCVolatilePhase1 temp = new TransactionStatePromotedNonMSDTCVolatilePhase1();
_transactionStatePromotedNonMSDTCVolatilePhase1 = temp;
}
}
}
return _transactionStatePromotedNonMSDTCVolatilePhase1;
}
}
protected static TransactionStatePromotedNonMSDTCSinglePhaseCommit _TransactionStatePromotedNonMSDTCSinglePhaseCommit
{
get
{
if (_transactionStatePromotedNonMSDTCSinglePhaseCommit == null)
{
lock (ClassSyncObject)
{
if (_transactionStatePromotedNonMSDTCSinglePhaseCommit == null)
{
TransactionStatePromotedNonMSDTCSinglePhaseCommit temp = new TransactionStatePromotedNonMSDTCSinglePhaseCommit();
_transactionStatePromotedNonMSDTCSinglePhaseCommit = temp;
}
}
}
return _transactionStatePromotedNonMSDTCSinglePhaseCommit;
}
}
protected static TransactionStatePromotedNonMSDTCAborted _TransactionStatePromotedNonMSDTCAborted
{
get
{
if (_transactionStatePromotedNonMSDTCAborted == null)
{
lock (ClassSyncObject)
{
if (_transactionStatePromotedNonMSDTCAborted == null)
{
TransactionStatePromotedNonMSDTCAborted temp = new TransactionStatePromotedNonMSDTCAborted();
_transactionStatePromotedNonMSDTCAborted = temp;
}
}
}
return _transactionStatePromotedNonMSDTCAborted;
}
}
protected static TransactionStatePromotedNonMSDTCCommitted _TransactionStatePromotedNonMSDTCCommitted
{
get
{
if (_transactionStatePromotedNonMSDTCCommitted == null)
{
lock (ClassSyncObject)
{
if (_transactionStatePromotedNonMSDTCCommitted == null)
{
TransactionStatePromotedNonMSDTCCommitted temp = new TransactionStatePromotedNonMSDTCCommitted();
_transactionStatePromotedNonMSDTCCommitted = temp;
}
}
}
return _transactionStatePromotedNonMSDTCCommitted;
}
}
protected static TransactionStatePromotedNonMSDTCIndoubt _TransactionStatePromotedNonMSDTCIndoubt
{
get
{
if (_transactionStatePromotedNonMSDTCIndoubt == null)
{
lock (ClassSyncObject)
{
if (_transactionStatePromotedNonMSDTCIndoubt == null)
{
TransactionStatePromotedNonMSDTCIndoubt temp = new TransactionStatePromotedNonMSDTCIndoubt();
_transactionStatePromotedNonMSDTCIndoubt = temp;
}
}
}
return _transactionStatePromotedNonMSDTCIndoubt;
}
}
// Helper object for static synchronization
internal static object ClassSyncObject
{
get
{
if ( classSyncObject == null )
{
object o = new object();
Interlocked.CompareExchange( ref classSyncObject, o, null );
}
return classSyncObject;
}
}
internal void CommonEnterState( InternalTransaction tx )
{
Debug.Assert( tx.State != this, "Changing to the same state." );
tx.State = this;
#if DEBUG
tx.stateHistory[tx.currentStateHist] = this;
if ( ++tx.currentStateHist > InternalTransaction.MaxStateHist )
{
tx.currentStateHist = 0;
}
#endif
}
// Every state must override EnterState
internal abstract void EnterState( InternalTransaction tx );
internal virtual void BeginCommit( InternalTransaction tx, bool asyncCommit, AsyncCallback asyncCallback, object asyncState )
{
throw TransactionException.CreateTransactionStateException( SR.GetString( SR.TraceSourceLtm ), tx.innerException, tx.DistributedTxId);
}
internal virtual void EndCommit( InternalTransaction tx )
{
Debug.Assert( false, string.Format( null, "Invalid Event for State; Current State: {0}", this.GetType() ));
throw TransactionException.CreateTransactionStateException( SR.GetString( SR.TraceSourceLtm ), tx.innerException, tx.DistributedTxId);
}
internal virtual void Rollback( InternalTransaction tx, Exception e )
{
throw TransactionException.CreateTransactionStateException( SR.GetString( SR.TraceSourceLtm ), tx.innerException, tx.DistributedTxId);
}
internal virtual Enlistment EnlistDurable(
InternalTransaction tx,
Guid resourceManagerIdentifier,
IEnlistmentNotification enlistmentNotification,
EnlistmentOptions enlistmentOptions,
Transaction atomicTransaction
)
{
throw TransactionException.CreateTransactionStateException( SR.GetString( SR.TraceSourceLtm ), tx.innerException, tx.DistributedTxId);
}
internal virtual Enlistment EnlistDurable(
InternalTransaction tx,
Guid resourceManagerIdentifier,
ISinglePhaseNotification enlistmentNotification,
EnlistmentOptions enlistmentOptions,
Transaction atomicTransaction
)
{
throw TransactionException.CreateTransactionStateException( SR.GetString( SR.TraceSourceLtm ), tx.innerException, tx.DistributedTxId);
}
internal virtual Enlistment EnlistVolatile(
InternalTransaction tx,
IEnlistmentNotification enlistmentNotification,
EnlistmentOptions enlistmentOptions,
Transaction atomicTransaction
)
{
throw TransactionException.CreateTransactionStateException( SR.GetString( SR.TraceSourceLtm ), tx.innerException, tx.DistributedTxId);
}
internal virtual Enlistment EnlistVolatile(
InternalTransaction tx,
ISinglePhaseNotification enlistmentNotification,
EnlistmentOptions enlistmentOptions,
Transaction atomicTransaction
)
{
throw TransactionException.CreateTransactionStateException( SR.GetString( SR.TraceSourceLtm ), tx.innerException, tx.DistributedTxId);
}
internal virtual void CheckForFinishedTransaction( InternalTransaction tx )
{
// Aborted & InDoubt states should throw exceptions.
}
// If a specific state does not have a story for identifiers then
// it simply gets a guid. This would be to handle cases like aborted
// and committed where the transaction has not been promoted and
// cannot be promoted so it doesn't matter what guid is returned.
//
// This leaves two specific sets of states that MUST override this...
// 1) Any state where the transaction could be promoted.
// 2) Any state where the transaction is already promoted.
internal virtual Guid get_Identifier( InternalTransaction tx )
{
return Guid.Empty;
}
// Every state derived from the base must override status
internal abstract TransactionStatus get_Status( InternalTransaction tx );
internal virtual void AddOutcomeRegistrant( InternalTransaction tx, TransactionCompletedEventHandler transactionCompletedDelegate )
{
throw TransactionException.CreateTransactionStateException( SR.GetString( SR.TraceSourceLtm ), tx.innerException, tx.DistributedTxId);
}
internal virtual void GetObjectData( InternalTransaction tx, SerializationInfo serializationInfo, StreamingContext context )
{
throw TransactionException.CreateTransactionStateException( SR.GetString( SR.TraceSourceLtm ), tx.innerException, tx.DistributedTxId);
}
internal virtual bool EnlistPromotableSinglePhase(
InternalTransaction tx,
IPromotableSinglePhaseNotification promotableSinglePhaseNotification,
Transaction atomicTransaction,
Guid promoterType
)
{
throw TransactionException.CreateTransactionStateException( SR.GetString( SR.TraceSourceLtm ), tx.innerException, tx.DistributedTxId);
}
internal virtual void CompleteBlockingClone( InternalTransaction tx )
{
}
internal virtual void CompleteAbortingClone( InternalTransaction tx )
{
}
internal virtual void CreateBlockingClone( InternalTransaction tx )
{
throw TransactionException.CreateTransactionStateException( SR.GetString( SR.TraceSourceLtm ), tx.innerException, tx.DistributedTxId);
}
internal virtual void CreateAbortingClone( InternalTransaction tx )
{
throw TransactionException.CreateTransactionStateException( SR.GetString( SR.TraceSourceLtm ), tx.innerException, tx.DistributedTxId);
}
internal virtual void ChangeStateTransactionAborted( InternalTransaction tx, Exception e )
{
Debug.Assert( false, string.Format( null, "Invalid Event for State; Current State: {0}", this.GetType() ));
if ( DiagnosticTrace.Error )
{
InvalidOperationExceptionTraceRecord.Trace( SR.GetString( SR.TraceSourceLtm ), "" );
}
throw new InvalidOperationException();
}
internal virtual void ChangeStateTransactionCommitted( InternalTransaction tx )
{
Debug.Assert( false, string.Format( null, "Invalid Event for State; Current State: {0}", this.GetType() ));
if ( DiagnosticTrace.Error )
{
InvalidOperationExceptionTraceRecord.Trace( SR.GetString( SR.TraceSourceLtm ), "" );
}
throw new InvalidOperationException();
}
internal virtual void InDoubtFromEnlistment( InternalTransaction tx )
{
Debug.Assert( false, string.Format( null, "Invalid Event for State; Current State: {0}", this.GetType() ));
if ( DiagnosticTrace.Error )
{
InvalidOperationExceptionTraceRecord.Trace( SR.GetString( SR.TraceSourceLtm ), "" );
}
throw new InvalidOperationException();
}
internal virtual void ChangeStatePromotedAborted( InternalTransaction tx )
{
Debug.Assert( false, string.Format( null, "Invalid Event for State; Current State: {0}", this.GetType() ));
if ( DiagnosticTrace.Error )
{
InvalidOperationExceptionTraceRecord.Trace( SR.GetString( SR.TraceSourceLtm ), "" );
}
throw new InvalidOperationException();
}
internal virtual void ChangeStatePromotedCommitted( InternalTransaction tx )
{
Debug.Assert( false, string.Format( null, "Invalid Event for State; Current State: {0}", this.GetType() ));
if ( DiagnosticTrace.Error )
{
InvalidOperationExceptionTraceRecord.Trace( SR.GetString( SR.TraceSourceLtm ), "" );
}
throw new InvalidOperationException();
}
internal virtual void InDoubtFromDtc( InternalTransaction tx )
{
Debug.Assert( false, string.Format( null, "Invalid Event for State; Current State: {0}", this.GetType() ));
if ( DiagnosticTrace.Error )
{
InvalidOperationExceptionTraceRecord.Trace( SR.GetString( SR.TraceSourceLtm ), "" );
}
throw new InvalidOperationException();
}
internal virtual void ChangeStatePromotedPhase0( InternalTransaction tx )
{
Debug.Assert( false, string.Format( null, "Invalid Event for State; Current State: {0}", this.GetType() ));
if ( DiagnosticTrace.Error )
{
InvalidOperationExceptionTraceRecord.Trace( SR.GetString( SR.TraceSourceLtm ), "" );
}
throw new InvalidOperationException();
}
internal virtual void ChangeStatePromotedPhase1( InternalTransaction tx )
{
Debug.Assert( false, string.Format( null, "Invalid Event for State; Current State: {0}", this.GetType() ));
if ( DiagnosticTrace.Error )
{
InvalidOperationExceptionTraceRecord.Trace( SR.GetString( SR.TraceSourceLtm ), "" );
}
throw new InvalidOperationException();
}
internal virtual void ChangeStateAbortedDuringPromotion( InternalTransaction tx )
{
Debug.Assert( false, string.Format( null, "Invalid Event for State; Current State: {0}", this.GetType() ));
if ( DiagnosticTrace.Error )
{
InvalidOperationExceptionTraceRecord.Trace( SR.GetString( SR.TraceSourceLtm ), "" );
}
throw new InvalidOperationException();
}
internal virtual void Timeout( InternalTransaction tx )
{
}
internal virtual void Phase0VolatilePrepareDone( InternalTransaction tx )
{
Debug.Assert( false, string.Format( null, "Invalid Event for State; Current State: {0}", this.GetType() ));
throw TransactionException.CreateTransactionStateException( SR.GetString( SR.TraceSourceLtm ), tx.innerException, tx.DistributedTxId);
}
internal virtual void Phase1VolatilePrepareDone( InternalTransaction tx )
{
Debug.Assert( false, string.Format( null, "Invalid Event for State; Current State: {0}", this.GetType() ));
throw TransactionException.CreateTransactionStateException( SR.GetString( SR.TraceSourceLtm ), tx.innerException, tx.DistributedTxId);
}
internal virtual void RestartCommitIfNeeded( InternalTransaction tx )
{
Debug.Assert( false, string.Format( null, "Invalid Event for State; Current State: {0}", this.GetType() ));
if ( DiagnosticTrace.Error )
{
InvalidOperationExceptionTraceRecord.Trace( SR.GetString( SR.TraceSourceLtm ), "" );
}
throw new InvalidOperationException( );
}
internal virtual bool ContinuePhase0Prepares()
{
return false;
}
internal virtual bool ContinuePhase1Prepares()
{
return false;
}
internal virtual void Promote( InternalTransaction tx )
{
throw TransactionException.CreateTransactionStateException( SR.GetString( SR.TraceSourceLtm ), tx.innerException, tx.DistributedTxId);
}
internal virtual byte[] PromotedToken( InternalTransaction tx )
{
throw TransactionException.CreateTransactionStateException(SR.GetString(SR.TraceSourceLtm), tx.innerException, tx.DistributedTxId);
}
internal virtual Enlistment PromoteAndEnlistDurable(
InternalTransaction tx,
Guid resourceManagerIdentifier,
IPromotableSinglePhaseNotification promotableNotification,
ISinglePhaseNotification enlistmentNotification,
EnlistmentOptions enlistmentOptions,
Transaction atomicTransaction
)
{
throw TransactionException.CreateTransactionStateException(SR.GetString(SR.TraceSourceLtm), tx.innerException, tx.DistributedTxId);
}
internal virtual void SetDistributedTransactionId(InternalTransaction tx,
IPromotableSinglePhaseNotification promotableNotification,
Guid distributedTransactionIdentifier)
{
throw TransactionException.CreateTransactionStateException(SR.GetString(SR.TraceSourceLtm), tx.innerException, tx.DistributedTxId);
}
internal virtual void DisposeRoot(InternalTransaction tx)
{
}
internal virtual bool IsCompleted( InternalTransaction tx )
{
tx.needPulse = true;
return false;
}
protected void AddVolatileEnlistment( ref VolatileEnlistmentSet enlistments, Enlistment enlistment )
{
// Grow the enlistment array if necessary.
if ( enlistments.volatileEnlistmentCount == enlistments.volatileEnlistmentSize )
{
InternalEnlistment[] newEnlistments =
new InternalEnlistment[enlistments.volatileEnlistmentSize + InternalTransaction.volatileArrayIncrement];
if ( enlistments.volatileEnlistmentSize > 0 )
{
Array.Copy(
enlistments.volatileEnlistments,
newEnlistments,
enlistments.volatileEnlistmentSize
);
}
enlistments.volatileEnlistmentSize += InternalTransaction.volatileArrayIncrement;
enlistments.volatileEnlistments = newEnlistments;
}
// Add a new element to the end of the list
enlistments.volatileEnlistments[enlistments.volatileEnlistmentCount] = enlistment.InternalEnlistment;
enlistments.volatileEnlistmentCount++;
// Make it's state active.
VolatileEnlistmentState._VolatileEnlistmentActive.EnterState(
enlistments.volatileEnlistments[enlistments.volatileEnlistmentCount - 1]);
}
}
// ActiveStates
//
// All states for which the transaction is not done should derive from this state.
internal abstract class ActiveStates : TransactionState
{
internal override TransactionStatus get_Status( InternalTransaction tx )
{
return TransactionStatus.Active;
}
internal override void AddOutcomeRegistrant( InternalTransaction tx, TransactionCompletedEventHandler transactionCompletedDelegate )
{
tx.transactionCompletedDelegate = (TransactionCompletedEventHandler)
System.Delegate.Combine( tx.transactionCompletedDelegate, transactionCompletedDelegate );
}
}
// EnlistableStates
//
// States for which it is ok to enlist.
internal abstract class EnlistableStates : ActiveStates
{
internal override Enlistment EnlistDurable(
InternalTransaction tx,
Guid resourceManagerIdentifier,
IEnlistmentNotification enlistmentNotification,
EnlistmentOptions enlistmentOptions,
Transaction atomicTransaction
)
{
tx.ThrowIfPromoterTypeIsNotMSDTC();
// Can't support an enlistment that dosn't support SPC
tx.promoteState.EnterState( tx );
// Note that just because we did an EnterState above does not mean that the state will be
// the same when the next method is called.
return tx.State.EnlistDurable( tx, resourceManagerIdentifier, enlistmentNotification, enlistmentOptions, atomicTransaction );
}
internal override Enlistment EnlistDurable(
InternalTransaction tx,
Guid resourceManagerIdentifier,
ISinglePhaseNotification enlistmentNotification,
EnlistmentOptions enlistmentOptions,
Transaction atomicTransaction
)
{
tx.ThrowIfPromoterTypeIsNotMSDTC();
if (tx.durableEnlistment != null || (enlistmentOptions & EnlistmentOptions.EnlistDuringPrepareRequired) != 0)
{
// These circumstances cause promotion
tx.promoteState.EnterState( tx );
return tx.State.EnlistDurable( tx, resourceManagerIdentifier, enlistmentNotification, enlistmentOptions, atomicTransaction );
}
// Create a durable enlistment
Enlistment en = new Enlistment( resourceManagerIdentifier, tx, enlistmentNotification, enlistmentNotification, atomicTransaction );
tx.durableEnlistment = en.InternalEnlistment;
DurableEnlistmentState._DurableEnlistmentActive.EnterState( tx.durableEnlistment );
if ( DiagnosticTrace.Information )
{
EnlistmentTraceRecord.Trace( SR.GetString( SR.TraceSourceLtm ),
tx.durableEnlistment.EnlistmentTraceId,
EnlistmentType.Durable,
EnlistmentOptions.None
);
}
return en;
}
internal override void Timeout( InternalTransaction tx )
{
if ( DiagnosticTrace.Warning )
{
TransactionTimeoutTraceRecord.Trace( SR.GetString( SR.TraceSourceLtm ),
tx.TransactionTraceId
);
}
TimeoutException e = new TimeoutException( SR.GetString( SR.TraceTransactionTimeout ));
this.Rollback( tx, e );
}
internal override void GetObjectData( InternalTransaction tx, SerializationInfo serializationInfo, StreamingContext context )
{
// This is not allowed if the transaction's PromoterType is not MSDTC.
tx.ThrowIfPromoterTypeIsNotMSDTC();
// Promote the transaction.
tx.promoteState.EnterState( tx );
// Forward this call
tx.State.GetObjectData( tx, serializationInfo, context );
}
internal override void CompleteBlockingClone( InternalTransaction tx )
{
// A blocking clone simulates a phase 0 volatile
// decrement the number of dependentClones
tx.phase0Volatiles.dependentClones--;
Debug.Assert( tx.phase0Volatiles.dependentClones >= 0 );
// Make certain we increment the right list.
Debug.Assert( tx.phase0Volatiles.preparedVolatileEnlistments <=
tx.phase0Volatiles.volatileEnlistmentCount + tx.phase0Volatiles.dependentClones );
// Check to see if all of the volatile enlistments are done.
if ( tx.phase0Volatiles.preparedVolatileEnlistments ==
tx.phase0VolatileWaveCount + tx.phase0Volatiles.dependentClones )
{
tx.State.Phase0VolatilePrepareDone( tx );
}
}
internal override void CompleteAbortingClone( InternalTransaction tx )
{
// A blocking clone simulates a phase 1 volatile
//
// Unlike a blocking clone however the aborting clones need to be accounted
// for specifically. So when one is complete remove it from the list.
tx.phase1Volatiles.dependentClones--;
Debug.Assert( tx.phase1Volatiles.dependentClones >= 0 );
}
internal override void CreateBlockingClone( InternalTransaction tx )
{
// A blocking clone simulates a phase 0 volatile
tx.phase0Volatiles.dependentClones++;
}
internal override void CreateAbortingClone( InternalTransaction tx )
{
// An aborting clone simulates a phase 1 volatile
tx.phase1Volatiles.dependentClones++;
}
internal override void Promote( InternalTransaction tx )
{
tx.promoteState.EnterState( tx );
tx.State.CheckForFinishedTransaction( tx );
}
internal override byte[] PromotedToken(InternalTransaction tx)
{
if (tx.promotedToken == null)
{
tx.promoteState.EnterState(tx);
tx.State.CheckForFinishedTransaction(tx);
}
return tx.promotedToken;
}
}
// TransactionStateActive
//
// Transaction state before commit has been called
internal class TransactionStateActive : EnlistableStates
{
internal override void EnterState( InternalTransaction tx )
{
// Set the transaction state
CommonEnterState( tx );
// Yeah it's active.
}
internal override void BeginCommit( InternalTransaction tx, bool asyncCommit, AsyncCallback asyncCallback, object asyncState )
{
// Store the given values
tx.asyncCommit = asyncCommit;
tx.asyncCallback = asyncCallback;
tx.asyncState = asyncState;
// Start the process for commit.
_TransactionStatePhase0.EnterState( tx );
}
internal override void Rollback( InternalTransaction tx, Exception e )
{
// Start the process for abort. From the active state we can transition directly
// to the aborted state.
if ( tx.innerException == null )
{
tx.innerException = e;
}
_TransactionStateAborted.EnterState( tx );
}
internal override Enlistment EnlistVolatile(
InternalTransaction tx,
IEnlistmentNotification enlistmentNotification,
EnlistmentOptions enlistmentOptions,
Transaction atomicTransaction
)
{
Enlistment enlistment = new Enlistment( tx, enlistmentNotification, null, atomicTransaction, enlistmentOptions );
if ( (enlistmentOptions & EnlistmentOptions.EnlistDuringPrepareRequired) != 0 )
{
AddVolatileEnlistment( ref tx.phase0Volatiles, enlistment );
}
else
{
AddVolatileEnlistment( ref tx.phase1Volatiles, enlistment );
}
if ( DiagnosticTrace.Information )
{
EnlistmentTraceRecord.Trace( SR.GetString( SR.TraceSourceLtm ),
enlistment.InternalEnlistment.EnlistmentTraceId,
EnlistmentType.Volatile,
enlistmentOptions
);
}
return enlistment;
}
internal override Enlistment EnlistVolatile(
InternalTransaction tx,
ISinglePhaseNotification enlistmentNotification,
EnlistmentOptions enlistmentOptions,
Transaction atomicTransaction
)
{
Enlistment enlistment = new Enlistment( tx, enlistmentNotification, enlistmentNotification, atomicTransaction, enlistmentOptions );
if ( (enlistmentOptions & EnlistmentOptions.EnlistDuringPrepareRequired) != 0 )
{
AddVolatileEnlistment( ref tx.phase0Volatiles, enlistment );
}
else
{
AddVolatileEnlistment( ref tx.phase1Volatiles, enlistment );
}
if ( DiagnosticTrace.Information )
{
EnlistmentTraceRecord.Trace( SR.GetString( SR.TraceSourceLtm ),
enlistment.InternalEnlistment.EnlistmentTraceId,
EnlistmentType.Volatile,
enlistmentOptions
);
}
return enlistment;
}
internal override bool EnlistPromotableSinglePhase(
InternalTransaction tx, IPromotableSinglePhaseNotification promotableSinglePhaseNotification,
Transaction atomicTransaction,
Guid promoterType
)
{
// Delegation will fail if there is a durable enlistment
if ( tx.durableEnlistment != null )
{
return false;
}
_TransactionStatePSPEOperation.PSPEInitialize( tx, promotableSinglePhaseNotification, promoterType );
// Create a durable enlistment.
Enlistment en = new Enlistment( tx, promotableSinglePhaseNotification, atomicTransaction );
tx.durableEnlistment = en.InternalEnlistment;
if ( DiagnosticTrace.Information )
{
EnlistmentTraceRecord.Trace( SR.GetString( SR.TraceSourceLtm ),
tx.durableEnlistment.EnlistmentTraceId,
EnlistmentType.PromotableSinglePhase,
EnlistmentOptions.None
);
}
// Specify the promoter for the transaction.
tx.promoter = promotableSinglePhaseNotification;
// Change the state that the transaction will promote to. Normally this would be simply
// be TransactionStatePromoted. However it now needs to promote to a delegated state.
// If the PromoterType is NOT TransactionInterop.PromoterTypeDtc, then the promoteState needs
// to be _TransactionStateDelegatedNonMSDTC.
// tx.PromoterType was set in PSPEInitialize.
Debug.Assert(tx.promoterType != Guid.Empty, "InternalTransaction.PromoterType was not set in PSPEInitialize");
if (tx.promoterType == TransactionInterop.PromoterTypeDtc)
{
tx.promoteState = _TransactionStateDelegated;
}
else
{
tx.promoteState = _TransactionStateDelegatedNonMSDTC;
}
// Pud the enlistment in an active state
DurableEnlistmentState._DurableEnlistmentActive.EnterState( tx.durableEnlistment );
// Hand back the enlistment.
return true;
}
// Volatile prepare is done for
internal override void Phase0VolatilePrepareDone( InternalTransaction tx )
{
// Ignore this event at the moment. It can be checked again in Phase0
}
internal override void Phase1VolatilePrepareDone( InternalTransaction tx )
{
// Ignore this event at the moment. It can be checked again in Phase1
}
internal override void DisposeRoot( InternalTransaction tx )
{
tx.State.Rollback( tx, null );
}
}
// TransactionStateSubordinateActive
//
// This is a transaction that is a very basic subordinate to some external TM.
internal class TransactionStateSubordinateActive : TransactionStateActive
{
// Every state must override EnterState
internal override void EnterState( InternalTransaction tx )
{
// Set the transaction state
CommonEnterState( tx );
Debug.Assert( tx.promoter != null, "Transaction Promoter is Null entering SubordinateActive" );
}
internal override void Rollback( InternalTransaction tx, Exception e )
{
// Start the process for abort. From the active state we can transition directly
// to the aborted state.
if ( tx.innerException == null )
{
tx.innerException = e;
}
((ISimpleTransactionSuperior)tx.promoter).Rollback();
_TransactionStateAborted.EnterState( tx );
}
internal override Enlistment EnlistVolatile(
InternalTransaction tx,
IEnlistmentNotification enlistmentNotification,
EnlistmentOptions enlistmentOptions,
Transaction atomicTransaction
)
{
tx.promoteState.EnterState( tx );
return tx.State.EnlistVolatile( tx, enlistmentNotification, enlistmentOptions, atomicTransaction );
}
internal override Enlistment EnlistVolatile(
InternalTransaction tx,
ISinglePhaseNotification enlistmentNotification,
EnlistmentOptions enlistmentOptions,
Transaction atomicTransaction
)
{
tx.promoteState.EnterState( tx );
return tx.State.EnlistVolatile( tx, enlistmentNotification, enlistmentOptions, atomicTransaction );
}
// Every state derived from the base must override status
internal override TransactionStatus get_Status( InternalTransaction tx )
{
tx.promoteState.EnterState( tx );
return tx.State.get_Status( tx );
}
internal override void AddOutcomeRegistrant( InternalTransaction tx, TransactionCompletedEventHandler transactionCompletedDelegate )
{
tx.promoteState.EnterState( tx );
tx.State.AddOutcomeRegistrant( tx, transactionCompletedDelegate );
}
internal override bool EnlistPromotableSinglePhase(
InternalTransaction tx,
IPromotableSinglePhaseNotification promotableSinglePhaseNotification,
Transaction atomicTransaction,
Guid promoterType
)
{
return false;
}
internal override void CreateBlockingClone( InternalTransaction tx )
{
tx.promoteState.EnterState( tx );
tx.State.CreateBlockingClone( tx );
}
internal override void CreateAbortingClone( InternalTransaction tx )
{
tx.promoteState.EnterState( tx );
tx.State.CreateAbortingClone( tx );
}
}
// TransactionStatePhase0
//
// A transaction that is in the beginning stage of committing.
internal class TransactionStatePhase0 : EnlistableStates
{
internal override void EnterState( InternalTransaction tx )
{
// Set the transaction state
CommonEnterState( tx );
// Get a copy of the current volatile enlistment count before entering this loop so that other
// threads don't affect the operation of this loop.
int volatileCount = tx.phase0Volatiles.volatileEnlistmentCount;
int dependentCount = tx.phase0Volatiles.dependentClones;
// Store the number of phase0 volatiles for this wave.
tx.phase0VolatileWaveCount = volatileCount;
// Check for volatile enlistments
if ( tx.phase0Volatiles.preparedVolatileEnlistments < volatileCount + dependentCount )
{
// Broadcast prepare to the phase 0 enlistments
for ( int i = 0; i < volatileCount; i++ )
{
tx.phase0Volatiles.volatileEnlistments[i].twoPhaseState.ChangeStatePreparing( tx.phase0Volatiles.volatileEnlistments[i]);
if ( !tx.State.ContinuePhase0Prepares() )
{
break;
}
}
}
else
{
// No volatile enlistments. Start phase 1.
_TransactionStateVolatilePhase1.EnterState( tx );
}
}
internal override Enlistment EnlistDurable(
InternalTransaction tx,
Guid resourceManagerIdentifier,
IEnlistmentNotification enlistmentNotification,
EnlistmentOptions enlistmentOptions,
Transaction atomicTransaction
)
{
tx.ThrowIfPromoterTypeIsNotMSDTC();
Enlistment en = base.EnlistDurable(tx, resourceManagerIdentifier, enlistmentNotification,
enlistmentOptions, atomicTransaction );
// Calling durable enlist in Phase0 may cause the transaction to promote. Leverage the promoted
tx.State.RestartCommitIfNeeded( tx );
return en;
}
internal override Enlistment EnlistDurable(
InternalTransaction tx,
Guid resourceManagerIdentifier,
ISinglePhaseNotification enlistmentNotification,
EnlistmentOptions enlistmentOptions,
Transaction atomicTransaction
)
{
tx.ThrowIfPromoterTypeIsNotMSDTC();
Enlistment en = base.EnlistDurable(tx, resourceManagerIdentifier, enlistmentNotification,
enlistmentOptions, atomicTransaction );
// Calling durable enlist in Phase0 may cause the transaction to promote. Leverage the promoted
tx.State.RestartCommitIfNeeded( tx );
return en;
}
internal override Enlistment EnlistVolatile(
InternalTransaction tx,
IEnlistmentNotification enlistmentNotification,
EnlistmentOptions enlistmentOptions,
Transaction atomicTransaction
)
{
Enlistment enlistment = new Enlistment( tx, enlistmentNotification, null, atomicTransaction, enlistmentOptions );
if ( (enlistmentOptions & EnlistmentOptions.EnlistDuringPrepareRequired) != 0 )
{
AddVolatileEnlistment( ref tx.phase0Volatiles, enlistment );
}
else
{
AddVolatileEnlistment( ref tx.phase1Volatiles, enlistment );
}
if ( DiagnosticTrace.Information )
{
EnlistmentTraceRecord.Trace( SR.GetString( SR.TraceSourceLtm ),
enlistment.InternalEnlistment.EnlistmentTraceId,
EnlistmentType.Volatile,
enlistmentOptions
);
}
return enlistment;
}
internal override Enlistment EnlistVolatile(
InternalTransaction tx,
ISinglePhaseNotification enlistmentNotification,
EnlistmentOptions enlistmentOptions,
Transaction atomicTransaction
)
{
Enlistment enlistment = new Enlistment( tx, enlistmentNotification, enlistmentNotification, atomicTransaction, enlistmentOptions );
if ( (enlistmentOptions & EnlistmentOptions.EnlistDuringPrepareRequired) != 0 )
{
AddVolatileEnlistment( ref tx.phase0Volatiles, enlistment );
}
else
{
AddVolatileEnlistment( ref tx.phase1Volatiles, enlistment );
}
if ( DiagnosticTrace.Information )
{
EnlistmentTraceRecord.Trace( SR.GetString( SR.TraceSourceLtm ),
enlistment.InternalEnlistment.EnlistmentTraceId,
EnlistmentType.Volatile,
enlistmentOptions
);
}
return enlistment;
}
internal override void Rollback( InternalTransaction tx, Exception e )
{
ChangeStateTransactionAborted( tx, e );
}
// Support PSPE enlistment during Phase0 prepare notification.
internal override bool EnlistPromotableSinglePhase(
InternalTransaction tx,
IPromotableSinglePhaseNotification promotableSinglePhaseNotification,
Transaction atomicTransaction,
Guid promoterType
)
{
// Delegation will fail if there is a durable enlistment
if ( tx.durableEnlistment != null )
{
return false;
}
// Initialize PSPE Operation and call initialize on IPromotableSinglePhaseNotification
_TransactionStatePSPEOperation.Phase0PSPEInitialize( tx, promotableSinglePhaseNotification, promoterType );
// Create a durable enlistment.
Enlistment en = new Enlistment( tx, promotableSinglePhaseNotification, atomicTransaction );
tx.durableEnlistment = en.InternalEnlistment;
if ( DiagnosticTrace.Information )
{
EnlistmentTraceRecord.Trace( SR.GetString( SR.TraceSourceLtm ),
tx.durableEnlistment.EnlistmentTraceId,
EnlistmentType.PromotableSinglePhase,
EnlistmentOptions.None
);
}
// Specify the promoter for the transaction.
tx.promoter = promotableSinglePhaseNotification;
// Change the state that the transaction will promote to. Normally this would be simply
// be TransactionStatePromoted. However it now needs to promote to a delegated state.
// If the PromoterType is NOT TransactionInterop.PromoterTypeDtc, then the promoteState needs
// to be _TransactionStateDelegatedNonMSDTC.
// tx.PromoterType was set in Phase0PSPEInitialize.
Debug.Assert(tx.promoterType != Guid.Empty, "InternalTransaction.PromoterType was not set in Phase0PSPEInitialize");
if (tx.promoterType == TransactionInterop.PromoterTypeDtc)
{
tx.promoteState = _TransactionStateDelegated;
}
else
{
tx.promoteState = _TransactionStateDelegatedNonMSDTC;
}
// Put the enlistment in an active state
DurableEnlistmentState._DurableEnlistmentActive.EnterState( tx.durableEnlistment );
// Hand back the enlistment.
return true;
}
// Volatile prepare is done for
internal override void Phase0VolatilePrepareDone( InternalTransaction tx )
{
// Check to see if any Phase0Volatiles have been added in Phase0.
// If so go through the list again.
// Get a copy of the current volatile enlistment count before entering this loop so that other
// threads don't affect the operation of this loop.
int volatileCount = tx.phase0Volatiles.volatileEnlistmentCount;
int dependentCount = tx.phase0Volatiles.dependentClones;
// Store the number of phase0 volatiles for this wave.
tx.phase0VolatileWaveCount = volatileCount;
// Check for volatile enlistments
if ( tx.phase0Volatiles.preparedVolatileEnlistments < volatileCount + dependentCount )
{
// Broadcast prepare to the phase 0 enlistments
for ( int i = 0; i < volatileCount; i++ )
{
tx.phase0Volatiles.volatileEnlistments[i].twoPhaseState.ChangeStatePreparing(tx.phase0Volatiles.volatileEnlistments[i]);
if ( !tx.State.ContinuePhase0Prepares() )
{
break;
}
}
}
else
{
// No volatile enlistments. Start phase 1.
_TransactionStateVolatilePhase1.EnterState( tx );
}
}
internal override void Phase1VolatilePrepareDone( InternalTransaction tx )
{
// Ignore this for now it can be checked again in Phase 1
}
internal override void RestartCommitIfNeeded( InternalTransaction tx )
{
// Commit does not need to be restarted
}
internal override bool ContinuePhase0Prepares()
{
return true;
}
internal override void Promote( InternalTransaction tx )
{
tx.promoteState.EnterState( tx );
tx.State.CheckForFinishedTransaction( tx );
tx.State.RestartCommitIfNeeded( tx );
}
internal override void ChangeStateTransactionAborted( InternalTransaction tx, Exception e )
{
if ( tx.innerException == null )
{
tx.innerException = e;
}
_TransactionStateAborted.EnterState( tx );
}
internal override void GetObjectData( InternalTransaction tx, SerializationInfo serializationInfo, StreamingContext context )
{
// This is not allowed if the transaction's PromoterType is not MSDTC.
tx.ThrowIfPromoterTypeIsNotMSDTC();
// Promote the transaction.
tx.promoteState.EnterState( tx );
// Forward this call
tx.State.GetObjectData( tx, serializationInfo, context );
// Restart the commit process.
tx.State.RestartCommitIfNeeded( tx );
}
}
// TransactionStateVolatilePhase1
//
// Represents the transaction state during phase 1 preparing volatile enlistments
internal class TransactionStateVolatilePhase1 : ActiveStates
{
internal override void EnterState( InternalTransaction tx )
{
// Set the transaction state
CommonEnterState( tx );
// Mark the committable transaction as complete.
tx.committableTransaction.complete = true;
// If at this point there are phase1 dependent clones abort the transaction
if ( tx.phase1Volatiles.dependentClones != 0 )
{
_TransactionStateAborted.EnterState( tx );
return;
}
if ( tx.phase1Volatiles.volatileEnlistmentCount == 1 && tx.durableEnlistment == null
&& tx.phase1Volatiles.volatileEnlistments[0].SinglePhaseNotification != null )
{
// This is really a case of SPC for volatiles
_TransactionStateVolatileSPC.EnterState( tx );
}
else if ( tx.phase1Volatiles.volatileEnlistmentCount > 0 )
{
// Broadcast prepare to the phase 0 enlistments
for ( int i = 0; i < tx.phase1Volatiles.volatileEnlistmentCount; i++ )
{
tx.phase1Volatiles.volatileEnlistments[i].twoPhaseState.ChangeStatePreparing(tx.phase1Volatiles.volatileEnlistments[i]);
if ( !tx.State.ContinuePhase1Prepares() )
{
break;
}
}
}
else
{
// No volatile phase 1 enlistments. Start phase durable SPC.
_TransactionStateSPC.EnterState( tx );
}
}
internal override void Rollback( InternalTransaction tx, Exception e )
{
ChangeStateTransactionAborted( tx, e );
}
internal override void ChangeStateTransactionAborted( InternalTransaction tx, Exception e )
{
if ( tx.innerException == null )
{
tx.innerException = e;
}
_TransactionStateAborted.EnterState( tx );
}
// Volatile prepare is done for
internal override void Phase1VolatilePrepareDone( InternalTransaction tx )
{
_TransactionStateSPC.EnterState( tx );
}
internal override bool ContinuePhase1Prepares()
{
return true;
}
internal override void Timeout( InternalTransaction tx )
{
if ( DiagnosticTrace.Warning )
{
TransactionTimeoutTraceRecord.Trace( SR.GetString( SR.TraceSourceLtm ),
tx.TransactionTraceId
);
}
TimeoutException e = new TimeoutException( SR.GetString( SR.TraceTransactionTimeout ));
this.Rollback( tx, e );
}
}
// TransactionStateVolatileSPC
//
// Represents the transaction state during phase 1 when issuing SPC to a volatile enlistment
internal class TransactionStateVolatileSPC : ActiveStates
{
internal override void EnterState( InternalTransaction tx )
{
// Set the transaction state
CommonEnterState( tx );
Debug.Assert( tx.phase1Volatiles.volatileEnlistmentCount == 1,
"There must be exactly 1 phase 1 volatile enlistment for TransactionStateVolatileSPC" );
tx.phase1Volatiles.volatileEnlistments[0].twoPhaseState.ChangeStateSinglePhaseCommit(
tx.phase1Volatiles.volatileEnlistments[0]);
}
internal override void ChangeStateTransactionCommitted( InternalTransaction tx )
{
// The durable enlistment must have committed. Go to the committed state.
_TransactionStateCommitted.EnterState( tx );
}
internal override void InDoubtFromEnlistment( InternalTransaction tx )
{
// The transaction is indoubt
_TransactionStateInDoubt.EnterState( tx );
}
internal override void ChangeStateTransactionAborted( InternalTransaction tx, Exception e )
{
if ( tx.innerException == null )
{
tx.innerException = e;
}
// The durable enlistment must have aborted. Go to the aborted state.
_TransactionStateAborted.EnterState( tx );
}
}
// TransactionStateSPC
//
// Represents the transaction state during phase 1
internal class TransactionStateSPC : ActiveStates
{
internal override void EnterState( InternalTransaction tx )
{
// Set the transaction state
CommonEnterState( tx );
// Check for a durable enlistment
if ( tx.durableEnlistment != null )
{
// Send SPC to the durable enlistment
tx.durableEnlistment.State.ChangeStateCommitting( tx.durableEnlistment );
}
else
{
// No durable enlistments. Go to the committed state.
_TransactionStateCommitted.EnterState( tx );
}
}
internal override void ChangeStateTransactionCommitted( InternalTransaction tx )
{
// The durable enlistment must have committed. Go to the committed state.
_TransactionStateCommitted.EnterState( tx );
}
internal override void InDoubtFromEnlistment( InternalTransaction tx )
{
// The transaction is indoubt
_TransactionStateInDoubt.EnterState( tx );
}
internal override void ChangeStateTransactionAborted( InternalTransaction tx, Exception e )
{
if ( tx.innerException == null )
{
tx.innerException = e;
}
// The durable enlistment must have aborted. Go to the aborted state.
_TransactionStateAborted.EnterState( tx );
}
}
// TransactionStateEnded
//
// This state indicates that the transaction is in some form of ended state.
internal abstract class TransactionStateEnded : TransactionState
{
internal override void EnterState( InternalTransaction tx )
{
if ( tx.needPulse )
{
System.Threading.Monitor.Pulse( tx );
}
}
internal override void AddOutcomeRegistrant( InternalTransaction tx, TransactionCompletedEventHandler transactionCompletedDelegate )
{
if ( transactionCompletedDelegate != null )
{
TransactionEventArgs args = new TransactionEventArgs();
args.transaction = tx.outcomeSource.InternalClone();
transactionCompletedDelegate( args.transaction, args );
}
}
internal override bool IsCompleted( InternalTransaction tx )
{
return true;
}
}
// TransactionStateAborted
//
// The transaction has been aborted. Abort is itempotent and can be called again but any
// other operations on the transaction should fail.
internal class TransactionStateAborted : TransactionStateEnded
{
internal override void EnterState( InternalTransaction tx )
{
base.EnterState( tx );
// Set the transaction state
CommonEnterState( tx );
// Do NOT mark the committable transaction as complete because it is aborting.
// Notify the enlistments that the transaction has aborted
for ( int i = 0; i < tx.phase0Volatiles.volatileEnlistmentCount; i++ )
{
tx.phase0Volatiles.volatileEnlistments[i].twoPhaseState.InternalAborted(tx.phase0Volatiles.volatileEnlistments[i]);
}
for ( int i = 0; i < tx.phase1Volatiles.volatileEnlistmentCount; i++ )
{
tx.phase1Volatiles.volatileEnlistments[i].twoPhaseState.InternalAborted(tx.phase1Volatiles.volatileEnlistments[i]);
}
// Notify the durable enlistment
if ( tx.durableEnlistment != null )
{
tx.durableEnlistment.State.InternalAborted( tx.durableEnlistment );
}
// Remove this from the timeout list
TransactionManager.TransactionTable.Remove( tx );
if ( DiagnosticTrace.Warning )
{
TransactionAbortedTraceRecord.Trace( SR.GetString( SR.TraceSourceLtm ),
tx.TransactionTraceId
);
}
// Fire Completion for anyone listening
tx.FireCompletion( );
// Check to see if we need to release some waiter.
if ( tx.asyncCommit )
{
tx.SignalAsyncCompletion();
}
}
internal override TransactionStatus get_Status( InternalTransaction tx )
{
return TransactionStatus.Aborted;
}
internal override void Rollback( InternalTransaction tx, Exception e )
{
// Abort is itempotent. Ignore this if the transaction is already aborted.
}
internal override void BeginCommit(InternalTransaction tx, bool asyncCommit, AsyncCallback asyncCallback, object asyncState)
{
// End Commit Must throw a TransactionAbortedException to let the caller know that the tx aborted.
throw CreateTransactionAbortedException( tx );
}
internal override void EndCommit( InternalTransaction tx )
{
// End Commit Must throw a TransactionAbortedException to let the caller know that the tx aborted.
throw CreateTransactionAbortedException( tx );
}
internal override void RestartCommitIfNeeded( InternalTransaction tx )
{
// Commit does not need to be restarted.
}
internal override void Timeout( InternalTransaction tx )
{
// The transaction has aborted already
}
// When all enlisments respond to prepare this event will fire.
internal override void Phase0VolatilePrepareDone( InternalTransaction tx )
{
// Since the transaction is aborted ignore it.
}
internal override void Phase1VolatilePrepareDone( InternalTransaction tx )
{
// Since the transaction is aborted ignore it.
}
internal override void ChangeStateTransactionAborted( InternalTransaction tx, Exception e )
{
// Yes, yes, yes... I already know.
}
internal override void ChangeStatePromotedAborted(InternalTransaction tx)
{
// The transaction must have aborted during promotion
}
internal override void ChangeStateAbortedDuringPromotion(InternalTransaction tx)
{
// This is fine too.
}
internal override void CreateBlockingClone( InternalTransaction tx )
{
throw CreateTransactionAbortedException( tx );
}
internal override void CreateAbortingClone( InternalTransaction tx )
{
throw CreateTransactionAbortedException( tx );
}
internal override void GetObjectData( InternalTransaction tx, SerializationInfo serializationInfo, StreamingContext context )
{
throw CreateTransactionAbortedException( tx );
}
internal override void CheckForFinishedTransaction( InternalTransaction tx )
{
throw CreateTransactionAbortedException( tx );
}
private TransactionException CreateTransactionAbortedException( InternalTransaction tx )
{
return TransactionAbortedException.Create( SR.GetString( SR.TraceSourceLtm), SR.GetString( SR.TransactionAborted ), tx.innerException, tx.DistributedTxId);
}
}
// TransactionStateCommitted
//
// This state indicates that the transaction has been committed. Basically any
// operations on the transaction should fail at this point.
internal class TransactionStateCommitted : TransactionStateEnded
{
internal override void EnterState( InternalTransaction tx )
{
base.EnterState( tx );
// Set the transaction state
CommonEnterState( tx );
// Notify the phase 0 enlistments that the transaction has aborted
for ( int i = 0; i < tx.phase0Volatiles.volatileEnlistmentCount; i++ )
{
tx.phase0Volatiles.volatileEnlistments[i].twoPhaseState.InternalCommitted(tx.phase0Volatiles.volatileEnlistments[i]);
}
// Notify the phase 1 enlistments that the transaction has aborted
for ( int i = 0; i < tx.phase1Volatiles.volatileEnlistmentCount; i++ )
{
tx.phase1Volatiles.volatileEnlistments[i].twoPhaseState.InternalCommitted(tx.phase1Volatiles.volatileEnlistments[i]);
}
// Remove this from the timeout list
TransactionManager.TransactionTable.Remove( tx );
if ( DiagnosticTrace.Verbose )
{
TransactionCommittedTraceRecord.Trace( SR.GetString( SR.TraceSourceLtm ),
tx.TransactionTraceId
);
}
// Fire Completion for anyone listening
tx.FireCompletion( );
// Check to see if we need to release some waiter.
if ( tx.asyncCommit )
{
tx.SignalAsyncCompletion();
}
}
internal override TransactionStatus get_Status( InternalTransaction tx )
{
return TransactionStatus.Committed;
}
internal override void Rollback( InternalTransaction tx, Exception e )
{
throw TransactionException.CreateTransactionStateException( SR.GetString( SR.TraceSourceLtm ), tx.innerException, tx.DistributedTxId);
}
internal override void EndCommit(InternalTransaction tx)
{
// End Commit does nothing because life is wonderful and we are happy!
}
}
// TransactionStateInDoubt
//
// This state indicates that the transaction is in doubt
internal class TransactionStateInDoubt : TransactionStateEnded
{
internal override void EnterState( InternalTransaction tx )
{
base.EnterState( tx );
// Set the transaction state
CommonEnterState( tx );
// Notify the phase 0 enlistments that the transaction has aborted
for ( int i = 0; i < tx.phase0Volatiles.volatileEnlistmentCount; i++ )
{
tx.phase0Volatiles.volatileEnlistments[i].twoPhaseState.InternalIndoubt(tx.phase0Volatiles.volatileEnlistments[i]);
}
// Notify the phase 1 enlistments that the transaction has aborted
for ( int i = 0; i < tx.phase1Volatiles.volatileEnlistmentCount; i++ )
{
tx.phase1Volatiles.volatileEnlistments[i].twoPhaseState.InternalIndoubt(tx.phase1Volatiles.volatileEnlistments[i]);
}
// Remove this from the timeout list
TransactionManager.TransactionTable.Remove( tx );
if ( DiagnosticTrace.Warning )
{
TransactionInDoubtTraceRecord.Trace( SR.GetString( SR.TraceSourceLtm ),
tx.TransactionTraceId
);
}
// Fire Completion for anyone listening
tx.FireCompletion( );
// Check to see if we need to release some waiter.
if ( tx.asyncCommit )
{
tx.SignalAsyncCompletion();
}
}
internal override TransactionStatus get_Status( InternalTransaction tx )
{
return TransactionStatus.InDoubt;
}
internal override void Rollback( InternalTransaction tx, Exception e )
{
throw TransactionException.CreateTransactionStateException( SR.GetString( SR.TraceSourceLtm ), tx.innerException, tx.DistributedTxId);
}
internal override void EndCommit(InternalTransaction tx)
{
throw TransactionInDoubtException.Create( SR.GetString( SR.TraceSourceBase), SR.GetString(SR.TransactionIndoubt), tx.innerException, tx.DistributedTxId);
}
internal override void CheckForFinishedTransaction( InternalTransaction tx )
{
throw TransactionInDoubtException.Create(SR.GetString(SR.TraceSourceBase), SR.GetString(SR.TransactionIndoubt), tx.innerException, tx.DistributedTxId);
}
internal override void GetObjectData( InternalTransaction tx, SerializationInfo serializationInfo, StreamingContext context )
{
throw TransactionInDoubtException.Create(SR.GetString(SR.TraceSourceBase), SR.GetString(SR.TransactionIndoubt), tx.innerException, tx.DistributedTxId);
}
}
// TransactionStatePromotedBase
//
// This is the base class for promoted states. It's main function is to pass calls
// through to the distributed transaction.
internal abstract class TransactionStatePromotedBase : TransactionState
{
internal override TransactionStatus get_Status( InternalTransaction tx )
{
// Since the distributed transaction manager will always tell the ltm about state
// changes via the enlistment that the Ltm has with it, the Ltm can tell client
// code what it thinks the state is on behalf of the distributed tm. Doing so
// prevents ----s with state changes of the promoted tx to the Ltm being
// told about those changes.
return TransactionStatus.Active;
}
internal override Enlistment EnlistVolatile(
InternalTransaction tx,
IEnlistmentNotification enlistmentNotification,
EnlistmentOptions enlistmentOptions,
Transaction atomicTransaction
)
{
Debug.Assert( tx.PromotedTransaction != null, "Promoted state not valid for transaction." );
// Don't get in the way for new volatile enlistments
// Don't hold locks while calling into the promoted tx
System.Threading.Monitor.Exit( tx );
try
{
Enlistment en = new Enlistment( enlistmentNotification, tx, atomicTransaction );
EnlistmentState._EnlistmentStatePromoted.EnterState( en.InternalEnlistment );
en.InternalEnlistment.PromotedEnlistment =
tx.PromotedTransaction.EnlistVolatile(
en.InternalEnlistment, enlistmentOptions );
return en;
}
finally
{
#pragma warning disable 0618
//@
System.Threading.Monitor.Enter(tx);
#pragma warning restore 0618
}
}
internal override Enlistment EnlistVolatile(
InternalTransaction tx,
ISinglePhaseNotification enlistmentNotification,
EnlistmentOptions enlistmentOptions,
Transaction atomicTransaction
)
{
Debug.Assert( tx.PromotedTransaction != null, "Promoted state not valid for transaction." );
// Don't get in the way for new volatile enlistments
// Don't hold locks while calling into the promoted tx
System.Threading.Monitor.Exit( tx );
try
{
Enlistment en = new Enlistment( enlistmentNotification, tx, atomicTransaction );
EnlistmentState._EnlistmentStatePromoted.EnterState( en.InternalEnlistment );
en.InternalEnlistment.PromotedEnlistment =
tx.PromotedTransaction.EnlistVolatile(
en.InternalEnlistment, enlistmentOptions );
return en;
}
finally
{
#pragma warning disable 0618
//@
System.Threading.Monitor.Enter(tx);
#pragma warning restore 0618
}
}
internal override Enlistment EnlistDurable(
InternalTransaction tx,
Guid resourceManagerIdentifier,
IEnlistmentNotification enlistmentNotification,
EnlistmentOptions enlistmentOptions,
Transaction atomicTransaction
)
{
Debug.Assert( tx.PromotedTransaction != null, "Promoted state not valid for transaction." );
tx.ThrowIfPromoterTypeIsNotMSDTC();
// Don't hold locks while calling into the promoted tx
System.Threading.Monitor.Exit( tx );
try
{
Enlistment en = new Enlistment(
resourceManagerIdentifier,
tx,
enlistmentNotification,
null,
atomicTransaction
);
EnlistmentState._EnlistmentStatePromoted.EnterState( en.InternalEnlistment );
en.InternalEnlistment.PromotedEnlistment =
tx.PromotedTransaction.EnlistDurable(
resourceManagerIdentifier,
(DurableInternalEnlistment)en.InternalEnlistment,
false,
enlistmentOptions
);
return en;
}
finally
{
#pragma warning disable 0618
//@
System.Threading.Monitor.Enter(tx);
#pragma warning restore 0618
}
}
internal override Enlistment EnlistDurable(
InternalTransaction tx,
Guid resourceManagerIdentifier,
ISinglePhaseNotification enlistmentNotification,
EnlistmentOptions enlistmentOptions,
Transaction atomicTransaction
)
{
Debug.Assert( tx.PromotedTransaction != null, "Promoted state not valid for transaction." );
tx.ThrowIfPromoterTypeIsNotMSDTC();
// Don't hold locks while calling into the promoted tx
System.Threading.Monitor.Exit( tx );
try
{
Enlistment en = new Enlistment(
resourceManagerIdentifier,
tx,
enlistmentNotification,
enlistmentNotification,
atomicTransaction
);
EnlistmentState._EnlistmentStatePromoted.EnterState( en.InternalEnlistment );
en.InternalEnlistment.PromotedEnlistment =
tx.PromotedTransaction.EnlistDurable(
resourceManagerIdentifier,
(DurableInternalEnlistment)en.InternalEnlistment,
true,
enlistmentOptions
);
return en;
}
finally
{
#pragma warning disable 0618
//@
System.Threading.Monitor.Enter(tx);
#pragma warning restore 0618
}
}
internal override void Rollback( InternalTransaction tx, Exception e )
{
Debug.Assert( tx.PromotedTransaction != null, "Promoted state not valid for transaction." );
// Forward this on to the promoted transaction.
if ( tx.innerException == null )
{
tx.innerException = e;
}
// Don't hold locks while calling into the promoted tx
System.Threading.Monitor.Exit( tx );
try
{
tx.PromotedTransaction.Rollback( );
}
finally
{
#pragma warning disable 0618
//@
System.Threading.Monitor.Enter(tx);
#pragma warning restore 0618
}
}
internal override Guid get_Identifier( InternalTransaction tx )
{
if (tx != null && tx.PromotedTransaction != null)
{
return tx.PromotedTransaction.Identifier;
}
else
{
return Guid.Empty;
}
}
internal override void AddOutcomeRegistrant( InternalTransaction tx, TransactionCompletedEventHandler transactionCompletedDelegate )
{
// Add this guy to the list of people to be notified of the outcome.
tx.transactionCompletedDelegate = (TransactionCompletedEventHandler)
System.Delegate.Combine( tx.transactionCompletedDelegate, transactionCompletedDelegate );
}
internal override void BeginCommit( InternalTransaction tx, bool asyncCommit, AsyncCallback asyncCallback, object asyncState )
{
// Store the given values
tx.asyncCommit = asyncCommit;
tx.asyncCallback = asyncCallback;
tx.asyncState = asyncState;
// Start the commit process.
_TransactionStatePromotedCommitting.EnterState( tx );
}
internal override void RestartCommitIfNeeded( InternalTransaction tx )
{
_TransactionStatePromotedP0Wave.EnterState( tx );
}
internal override bool EnlistPromotableSinglePhase(
InternalTransaction tx, IPromotableSinglePhaseNotification promotableSinglePhaseNotification,
Transaction atomicTransaction,
Guid promoterType
)
{
// The transaction has been promoted and cannot support a promotable singe phase enlistment
return false;
}
internal override void CompleteBlockingClone( InternalTransaction tx )
{
// First try to complete one of the internal blocking clones
if ( tx.phase0Volatiles.dependentClones > 0 )
{
// decrement the number of clones
tx.phase0Volatiles.dependentClones--;
// Make certain we increment the right list.
Debug.Assert( tx.phase0Volatiles.preparedVolatileEnlistments <=
tx.phase0Volatiles.volatileEnlistmentCount + tx.phase0Volatiles.dependentClones );
// Check to see if all of the volatile enlistments are done.
if ( tx.phase0Volatiles.preparedVolatileEnlistments ==
tx.phase0VolatileWaveCount + tx.phase0Volatiles.dependentClones )
{
tx.State.Phase0VolatilePrepareDone( tx );
}
}
else
{
// Otherwise this must be a dependent clone created after promotion
tx.phase0WaveDependentCloneCount--;
Debug.Assert( tx.phase0WaveDependentCloneCount >= 0 );
if ( tx.phase0WaveDependentCloneCount == 0 )
{
Oletx.OletxDependentTransaction dtx = tx.phase0WaveDependentClone;
tx.phase0WaveDependentClone = null;
System.Threading.Monitor.Exit( tx );
try
{
try
{
dtx.Complete();
}
finally
{
dtx.Dispose();
}
}
finally
{
#pragma warning disable 0618
//@
System.Threading.Monitor.Enter(tx);
#pragma warning restore 0618
}
}
}
}
internal override void CompleteAbortingClone( InternalTransaction tx )
{
// If we have a phase1Volatile.VolatileDemux, we have a phase1 volatile enlistment
// on the promoted transaction and it will take care of checking for incomplete aborting
// dependent clones in its Prepare processing.
if ( null != tx.phase1Volatiles.VolatileDemux )
{
tx.phase1Volatiles.dependentClones--;
Debug.Assert( tx.phase1Volatiles.dependentClones >= 0 );
}
else
// We need to deal with the aborting clones ourself, possibly completing the aborting
// clone we have on the promoted transaction.
{
tx.abortingDependentCloneCount--;
Debug.Assert( 0 <= tx.abortingDependentCloneCount );
if ( 0 == tx.abortingDependentCloneCount )
{
// We need to complete our dependent clone on the promoted transaction and null it out
// so if we get a new one, a new one will be created on the promoted transaction.
Oletx.OletxDependentTransaction dtx = tx.abortingDependentClone;
tx.abortingDependentClone = null;
System.Threading.Monitor.Exit( tx );
try
{
try
{
dtx.Complete();
}
finally
{
dtx.Dispose();
}
}
finally
{
#pragma warning disable 0618
//@
System.Threading.Monitor.Enter(tx);
#pragma warning restore 0618
}
}
}
}
internal override void CreateBlockingClone( InternalTransaction tx )
{
// Once the transaction is promoted leverage the distributed
// transaction manager for blocking dependent clones so that they
// will handle phase 0 waves.
if ( tx.phase0WaveDependentClone == null )
{
tx.phase0WaveDependentClone = tx.PromotedTransaction.DependentClone( true );
}
tx.phase0WaveDependentCloneCount++;
}
internal override void CreateAbortingClone( InternalTransaction tx )
{
// If we have a VolatileDemux in phase1Volatiles, then we have a phase1 volatile enlistment
// on the promoted transaction, so we can depend on that to deal with our aborting dependent clones.
if ( null != tx.phase1Volatiles.VolatileDemux )
{
tx.phase1Volatiles.dependentClones++;
}
else
// We promoted without creating a phase1 volatile enlistment on the promoted transaction,
// so we let the promoted transaction deal with the aboring clone.
{
if ( null == tx.abortingDependentClone )
{
tx.abortingDependentClone = tx.PromotedTransaction.DependentClone( false );
}
tx.abortingDependentCloneCount++;
}
}
internal override bool ContinuePhase0Prepares()
{
return true;
}
internal override void GetObjectData( InternalTransaction tx, SerializationInfo serializationInfo, StreamingContext context )
{
Debug.Assert( tx.PromotedTransaction != null, "Promoted state not valid for transaction." );
// This is not allowed if the transaction's PromoterType is not MSDTC.
tx.ThrowIfPromoterTypeIsNotMSDTC();
// Simply get call get object data for the promoted transaction.
ISerializable serializableTx = tx.PromotedTransaction as ISerializable;
if ( serializableTx == null )
{
// The LTM can only support this if the Distributed TM Supports it.
throw new NotSupportedException();
}
// Before forwarding this call to the promoted tx make sure to change
// the full type info so that only if the promoted tx does not set this
// then it should be set correctly.
serializationInfo.FullTypeName = tx.PromotedTransaction.GetType().FullName;
// Now forward the call.
serializableTx.GetObjectData( serializationInfo, context );
}
internal override void ChangeStatePromotedAborted( InternalTransaction tx )
{
_TransactionStatePromotedAborted.EnterState( tx );
}
internal override void ChangeStatePromotedCommitted( InternalTransaction tx )
{
_TransactionStatePromotedCommitted.EnterState( tx );
}
internal override void InDoubtFromDtc( InternalTransaction tx )
{
_TransactionStatePromotedIndoubt.EnterState( tx );
}
internal override void InDoubtFromEnlistment(InternalTransaction tx)
{
_TransactionStatePromotedIndoubt.EnterState( tx );
}
internal override void ChangeStateAbortedDuringPromotion( InternalTransaction tx )
{
_TransactionStateAborted.EnterState( tx );
}
internal override void Timeout( InternalTransaction tx )
{
// LTM gives up the ability to control Tx timeout when it promotes.
try
{
if ( tx.innerException == null )
{
tx.innerException = new TimeoutException( SR.GetString( SR.TraceTransactionTimeout ));;
}
tx.PromotedTransaction.Rollback();
if ( DiagnosticTrace.Warning )
{
TransactionTimeoutTraceRecord.Trace( SR.GetString( SR.TraceSourceLtm ),
tx.TransactionTraceId
);
}
}
catch ( TransactionException te )
{
// This could fail for any number of reasons based on the state of the transaction.
// The Ltm tries anyway because PSPE transactions have no timeout specified and some
// distributed transaction managers may not honer the timeout correctly.
// The exception needs to be caught because we don't want it to go unhandled on the
// timer thread.
if ( DiagnosticTrace.Verbose )
{
ExceptionConsumedTraceRecord.Trace( SR.GetString( SR.TraceSourceLtm ),
te );
}
}
}
internal override void Promote( InternalTransaction tx )
{
// do nothing, we are already promoted
}
internal override byte[] PromotedToken(InternalTransaction tx)
{
// Since we are in TransactionStatePromotedBase or one if its derived classes, we
// must already be promoted. So return the InternalTransaction's promotedToken.
Debug.Assert(tx.promotedToken != null, "InternalTransaction.promotedToken is null in TransactionStateDelegatedNonMSDTCBase or one of its derived classes.");
return tx.promotedToken;
}
internal override void Phase0VolatilePrepareDone( InternalTransaction tx )
{
// Early done notifications may come from volatiles at any time.
// The state machine will handle all enlistments being complete in later phases.
}
internal override void Phase1VolatilePrepareDone( InternalTransaction tx )
{
// Early done notifications may come from volatiles at any time.
// The state machine will handle all enlistments being complete in later phases.
}
}
// TransactionStateNonCommittablePromoted
//
// This state indicates that the transaction has been promoted and all further actions on
// the transaction should be forwarded to the promoted transaction.
internal class TransactionStateNonCommittablePromoted : TransactionStatePromotedBase
{
internal override void EnterState( InternalTransaction tx )
{
// Set the transaction state
CommonEnterState( tx );
// Let the distributed transaction know that we want to know about the outcome.
tx.PromotedTransaction.realOletxTransaction.InternalTransaction = tx;
}
}
// TransactionStatePromoted
//
// This state indicates that the transaction has been promoted and all further actions on
// the transaction should be forwarded to the promoted transaction.
internal class TransactionStatePromoted : TransactionStatePromotedBase
{
internal override void EnterState( InternalTransaction tx )
{
Debug.Assert((tx.promoterType == Guid.Empty) || (tx.promoterType == TransactionInterop.PromoterTypeDtc), "Promoted to MSTC but PromoterType is not TransactionInterop.PromoterTypeDtc");
// The promoterType may not yet be set. This state assumes we are promoting to MSDTC.
tx.SetPromoterTypeToMSDTC();
if ( tx.outcomeSource.isoLevel == IsolationLevel.Snapshot )
{
throw TransactionException.CreateInvalidOperationException( SR.GetString( SR.TraceSourceLtm ),
SR.GetString( SR.CannotPromoteSnapshot ), null);
}
// Set the transaction state
CommonEnterState( tx );
// Create a transaction with the distributed transaction manager
Oletx.OletxCommittableTransaction distributedTx = null;
try
{
TimeSpan newTimeout;
if ( tx.AbsoluteTimeout == long.MaxValue )
{
// The transaction has no timeout
newTimeout = TimeSpan.Zero;
}
else
{
newTimeout = TransactionManager.TransactionTable.RecalcTimeout( tx );
if ( newTimeout <= TimeSpan.Zero )
{
return;
}
}
// Just create a new transaction.
TransactionOptions options = new TransactionOptions();
options.IsolationLevel = tx.outcomeSource.isoLevel;
options.Timeout = newTimeout;
// Create a new distributed transaction.
distributedTx =
TransactionManager.DistributedTransactionManager.CreateTransaction( options );
distributedTx.savedLtmPromotedTransaction = tx.outcomeSource;
if ( DiagnosticTrace.Information )
{
TransactionPromotedTraceRecord.Trace( SR.GetString( SR.TraceSourceLtm ),
tx.TransactionTraceId,
distributedTx.TransactionTraceId
);
}
}
catch ( TransactionException te )
{
// There was an exception trying to create the distributed transaction.
// Save the exception and let the transaction get aborted by the finally block.
tx.innerException = te;
if ( DiagnosticTrace.Verbose )
{
ExceptionConsumedTraceRecord.Trace( SR.GetString( SR.TraceSourceLtm ),
te );
}
return;
}
finally
{
if ( distributedTx == null )
{
// There was an exception trying to create the distributed transaction abort
// the local transaction and exit.
tx.State.ChangeStateAbortedDuringPromotion( tx );
}
}
// Associate the distributed transaction with the local transaction.
tx.PromotedTransaction = distributedTx;
// Add a weak reference to the transaction to the promotedTransactionTable.
Hashtable promotedTransactionTable = TransactionManager.PromotedTransactionTable;
lock ( promotedTransactionTable )
{
// Since we are adding this reference to the table create an object that will clean that
// entry up.
tx.finalizedObject = new FinalizedObject( tx, distributedTx.Identifier );
WeakReference weakRef = new WeakReference( tx.outcomeSource, false );
promotedTransactionTable[distributedTx.Identifier] = weakRef;
}
TransactionManager.FireDistributedTransactionStarted( tx.outcomeSource );
// Once we have a promoted transaction promote the enlistments.
PromoteEnlistmentsAndOutcome( tx );
}
protected bool PromotePhaseVolatiles(
InternalTransaction tx,
ref VolatileEnlistmentSet volatiles,
bool phase0 )
{
if ( volatiles.volatileEnlistmentCount + volatiles.dependentClones > 0 )
{
if ( phase0 )
{
// Create a volatile demultiplexer for the transaction
volatiles.VolatileDemux = new Phase0VolatileDemultiplexer( tx );
}
else
{
// Create a volatile demultiplexer for the transaction
volatiles.VolatileDemux = new Phase1VolatileDemultiplexer( tx );
}
volatiles.VolatileDemux.oletxEnlistment = tx.PromotedTransaction.EnlistVolatile( volatiles.VolatileDemux,
phase0 ? EnlistmentOptions.EnlistDuringPrepareRequired : EnlistmentOptions.None );
}
return true;
}
internal virtual bool PromoteDurable( InternalTransaction tx )
{
// Promote the durable enlistment if one exists.
if ( tx.durableEnlistment != null )
{
// Directly enlist the durable enlistment with the resource manager.
InternalEnlistment enlistment = tx.durableEnlistment;
IPromotedEnlistment oletxEnlistment = tx.PromotedTransaction.EnlistDurable(
enlistment.ResourceManagerIdentifier,
(DurableInternalEnlistment)enlistment,
enlistment.SinglePhaseNotification != null,
EnlistmentOptions.None
);
// Promote the enlistment.
tx.durableEnlistment.State.ChangeStatePromoted( tx.durableEnlistment, oletxEnlistment );
}
return true;
}
internal virtual void PromoteEnlistmentsAndOutcome( InternalTransaction tx )
{
// Failures from this point on will simply abort the two types of transaction
// seperately. Note that this may cause duplicate internal aborted events to
// be sent to some of the enlistments however the enlistment state machines
// can handle the duplicate notification.
bool enlistmentsPromoted = false;
// Tell the RealOletxTransaction that we want a callback for the outcome.
tx.PromotedTransaction.RealTransaction.InternalTransaction = tx;
// Promote Phase 0 Volatiles
try
{
enlistmentsPromoted = PromotePhaseVolatiles( tx, ref tx.phase0Volatiles, true );
}
catch ( TransactionException te )
{
//
// Record the exception information.
tx.innerException = te;
if ( DiagnosticTrace.Verbose )
{
ExceptionConsumedTraceRecord.Trace( SR.GetString( SR.TraceSourceLtm ),
te );
}
return;
}
finally
{
if ( !enlistmentsPromoted )
{
tx.PromotedTransaction.Rollback( );
// Now abort this transaction.
tx.State.ChangeStateAbortedDuringPromotion( tx );
}
}
enlistmentsPromoted = false;
try
{
enlistmentsPromoted = PromotePhaseVolatiles( tx, ref tx.phase1Volatiles, false );
}
catch ( TransactionException te )
{
// Record the exception information.
tx.innerException = te;
if ( DiagnosticTrace.Verbose )
{
ExceptionConsumedTraceRecord.Trace( SR.GetString( SR.TraceSourceLtm ),
te );
}
return;
}
finally
{
if ( !enlistmentsPromoted )
{
tx.PromotedTransaction.Rollback( );
// Now abort this transaction.
tx.State.ChangeStateAbortedDuringPromotion( tx );
}
}
enlistmentsPromoted = false;
// Promote the durable enlistment
try
{
enlistmentsPromoted = PromoteDurable( tx );
}
catch ( TransactionException te )
{
// Record the exception information.
tx.innerException = te;
if ( DiagnosticTrace.Verbose )
{
ExceptionConsumedTraceRecord.Trace( SR.GetString( SR.TraceSourceLtm ),
te );
}
return;
}
finally
{
if ( !enlistmentsPromoted )
{
tx.PromotedTransaction.Rollback( );
// Now abort this transaction.
tx.State.ChangeStateAbortedDuringPromotion( tx );
}
}
}
internal override void DisposeRoot( InternalTransaction tx )
{
tx.State.Rollback( tx, null );
}
}
// TransactionStatePromotedP0Wave
//
// This state indicates that the transaction has been promoted during phase 0. This
// is a holding state until the current phase 0 wave is complete. When the current
// wave is complete the state changes to committing.
internal class TransactionStatePromotedP0Wave : TransactionStatePromotedBase
{
internal override void EnterState( InternalTransaction tx )
{
CommonEnterState( tx );
}
internal override void BeginCommit( InternalTransaction tx, bool asyncCommit, AsyncCallback asyncCallback, object asyncState )
{
// Don't allow this again.
throw TransactionException.CreateTransactionStateException( SR.GetString( SR.TraceSourceLtm ), tx.innerException, tx.DistributedTxId);
}
internal override void Phase0VolatilePrepareDone( InternalTransaction tx )
{
try
{
// Now that the previous wave is done continue start committing the transaction.
_TransactionStatePromotedCommitting.EnterState( tx );
}
catch ( TransactionException e )
{
// In this state we don't want a transaction exception from BeginCommit to randomly
// bubble up to the application or go unhandled. So catch the exception and if the
// inner exception for the transaction has not already been set then set it.
if ( tx.innerException == null )
{
tx.innerException = e;
}
if ( DiagnosticTrace.Verbose )
{
ExceptionConsumedTraceRecord.Trace( SR.GetString( SR.TraceSourceLtm ),
e );
}
}
}
internal override bool ContinuePhase0Prepares()
{
return true;
}
internal override void ChangeStateTransactionAborted( InternalTransaction tx, Exception e )
{
if ( tx.innerException == null )
{
tx.innerException = e;
}
// This change state event at this point would be caused by one of the enlistments
// aborting. Really change to P0Aborting
_TransactionStatePromotedP0Aborting.EnterState( tx );
}
}
// TransactionStatePromotedCommitting
//
// The transaction has been promoted but is in the process of committing.
internal class TransactionStatePromotedCommitting : TransactionStatePromotedBase
{
internal override void EnterState( InternalTransaction tx )
{
CommonEnterState( tx );
// Use the asynchronous commit provided by the promoted transaction
Oletx.OletxCommittableTransaction ctx = (Oletx.OletxCommittableTransaction)tx.PromotedTransaction;
ctx.BeginCommit( tx );
}
internal override void BeginCommit( InternalTransaction tx, bool asyncCommit, AsyncCallback asyncCallback, object asyncState )
{
// Don't allow this again.
throw TransactionException.CreateTransactionStateException( SR.GetString( SR.TraceSourceLtm ), tx.innerException, tx.DistributedTxId);
}
internal override void ChangeStatePromotedPhase0( InternalTransaction tx )
{
_TransactionStatePromotedPhase0.EnterState( tx );
}
internal override void ChangeStatePromotedPhase1( InternalTransaction tx )
{
_TransactionStatePromotedPhase1.EnterState( tx );
}
}
// TransactionStatePromotedPhase0
//
// This state indicates that the transaction has been promoted and started the process
// of committing. The transaction had volatile phase0 enlistments and is acting as a
// proxy to the TM for those phase0 enlistments.
internal class TransactionStatePromotedPhase0 : TransactionStatePromotedCommitting
{
internal override void EnterState( InternalTransaction tx )
{
CommonEnterState( tx );
// Get a copy of the current volatile enlistment count before entering this loop so that other
// threads don't affect the operation of this loop.
int volatileCount = tx.phase0Volatiles.volatileEnlistmentCount;
int dependentCount = tx.phase0Volatiles.dependentClones;
// Store the number of phase0 volatiles for this wave.
tx.phase0VolatileWaveCount = volatileCount;
// Check to see if we still need to send out volatile prepare notifications or if
// they are all done. They may be done if the transaction was already in phase 0
// before it got promoted.
if ( tx.phase0Volatiles.preparedVolatileEnlistments <
volatileCount + dependentCount )
{
// Broadcast preprepare to the volatile subordinates
for ( int i = 0; i < volatileCount; i++ )
{
tx.phase0Volatiles.volatileEnlistments[i].twoPhaseState.ChangeStatePreparing(
tx.phase0Volatiles.volatileEnlistments[i]);
if ( !tx.State.ContinuePhase0Prepares() )
{
break;
}
}
}
else
{
Phase0VolatilePrepareDone( tx );
}
}
internal override void Phase0VolatilePrepareDone( InternalTransaction tx )
{
Debug.Assert( tx.phase0Volatiles.VolatileDemux != null, "Volatile Demux must exist for VolatilePrepareDone when promoted." );
System.Threading.Monitor.Exit( tx );
try
{
// Tell the distributed TM that the volatile enlistments are prepared
tx.phase0Volatiles.VolatileDemux.oletxEnlistment.Prepared();
}
finally
{
#pragma warning disable 0618
//@
System.Threading.Monitor.Enter(tx);
#pragma warning restore 0618
}
}
internal override bool ContinuePhase0Prepares()
{
return true;
}
internal override void ChangeStateTransactionAborted( InternalTransaction tx, Exception e )
{
if ( tx.innerException == null )
{
tx.innerException = e;
}
// This change state event at this point would be caused by one of the enlistments
// aborting. Really change to P0Aborting
_TransactionStatePromotedP0Aborting.EnterState( tx );
}
}
// TransactionStatePromotedPhase1
//
// This state indicates that the transaction has been promoted and started the process
// of committing. The transaction had volatile phase1 enlistments and is acting as a
// proxy to the TM for those phase1 enlistments.
internal class TransactionStatePromotedPhase1 : TransactionStatePromotedCommitting
{
internal override void EnterState( InternalTransaction tx )
{
CommonEnterState( tx );
if ( tx.committableTransaction != null )
{
// If we have a committable transaction then mark it as complete.
tx.committableTransaction.complete = true;
}
// If at this point there are phase1 dependent clones abort the transaction
if ( tx.phase1Volatiles.dependentClones != 0 )
{
tx.State.ChangeStateTransactionAborted( tx, null );
return;
}
// Get a copy of the current volatile enlistment count before entering this loop so that other
// threads don't affect the operation of this loop.
int volatileCount = tx.phase1Volatiles.volatileEnlistmentCount;
// Check to see if we still need to send out volatile prepare notifications or if
// they are all done. They may be done if the transaction was already in phase 0
// before it got promoted.
if ( tx.phase1Volatiles.preparedVolatileEnlistments < volatileCount )
{
// Broadcast preprepare to the volatile subordinates
for ( int i = 0; i < volatileCount; i++ )
{
tx.phase1Volatiles.volatileEnlistments[i].twoPhaseState.ChangeStatePreparing(
tx.phase1Volatiles.volatileEnlistments[i]);
if ( !tx.State.ContinuePhase1Prepares() )
{
break;
}
}
}
else
{
Phase1VolatilePrepareDone( tx );
}
}
internal override void CreateBlockingClone( InternalTransaction tx )
{
throw TransactionException.CreateTransactionStateException( SR.GetString( SR.TraceSourceLtm ), tx.innerException, tx.DistributedTxId);
}
internal override void CreateAbortingClone( InternalTransaction tx )
{
throw TransactionException.CreateTransactionStateException( SR.GetString( SR.TraceSourceLtm ), tx.innerException, tx.DistributedTxId);
}
internal override void ChangeStateTransactionAborted( InternalTransaction tx, Exception e )
{
if ( tx.innerException == null )
{
tx.innerException = e;
}
// This change state event at this point would be caused by one of the enlistments
// aborting. Really change to P1Aborting
_TransactionStatePromotedP1Aborting.EnterState( tx );
}
internal override void Phase1VolatilePrepareDone( InternalTransaction tx )
{
Debug.Assert( tx.phase1Volatiles.VolatileDemux != null, "Volatile Demux must exist for VolatilePrepareDone when promoted." );
System.Threading.Monitor.Exit( tx );
try
{
// Tell the distributed TM that the volatile enlistments are prepared
tx.phase1Volatiles.VolatileDemux.oletxEnlistment.Prepared();
}
finally
{
#pragma warning disable 0618
//@
System.Threading.Monitor.Enter(tx);
#pragma warning restore 0618
}
}
internal override bool ContinuePhase1Prepares()
{
return true;
}
internal override Enlistment EnlistVolatile( InternalTransaction tx, IEnlistmentNotification enlistmentNotification, EnlistmentOptions enlistmentOptions, Transaction atomicTransaction )
{
throw TransactionException.Create(SR.GetString(SR.TooLate), tx == null ? Guid.Empty : tx.DistributedTxId);
}
internal override Enlistment EnlistVolatile( InternalTransaction tx, ISinglePhaseNotification enlistmentNotification, EnlistmentOptions enlistmentOptions, Transaction atomicTransaction )
{
throw TransactionException.Create(SR.GetString(SR.TooLate), tx == null ? Guid.Empty : tx.DistributedTxId);
}
internal override Enlistment EnlistDurable( InternalTransaction tx, Guid resourceManagerIdentifier, IEnlistmentNotification enlistmentNotification, EnlistmentOptions enlistmentOptions, Transaction atomicTransaction )
{
throw TransactionException.Create(SR.GetString(SR.TooLate), tx == null ? Guid.Empty : tx.DistributedTxId);
}
internal override Enlistment EnlistDurable( InternalTransaction tx, Guid resourceManagerIdentifier, ISinglePhaseNotification enlistmentNotification, EnlistmentOptions enlistmentOptions, Transaction atomicTransaction )
{
throw TransactionException.Create(SR.GetString(SR.TooLate), tx == null ? Guid.Empty : tx.DistributedTxId);
}
}
// TransactionStatePromotedAborting
//
// This state indicates that the transaction has been promoted but aborted. Once the volatile
// enlistments have finished responding the tx can be finished.
internal abstract class TransactionStatePromotedAborting : TransactionStatePromotedBase
{
internal override void EnterState( InternalTransaction tx )
{
CommonEnterState( tx );
}
internal override TransactionStatus get_Status( InternalTransaction tx )
{
return TransactionStatus.Aborted;
}
internal override void BeginCommit( InternalTransaction tx, bool asyncCommit, AsyncCallback asyncCallback, object asyncState )
{
// Don't allow this again.
throw TransactionException.CreateTransactionStateException( SR.GetString( SR.TraceSourceLtm ), tx.innerException, tx.DistributedTxId);
}
internal override void CreateBlockingClone( InternalTransaction tx )
{
throw TransactionException.CreateTransactionStateException( SR.GetString( SR.TraceSourceLtm ), tx.innerException, tx.DistributedTxId);
}
internal override void CreateAbortingClone( InternalTransaction tx )
{
throw TransactionException.CreateTransactionStateException( SR.GetString( SR.TraceSourceLtm ), tx.innerException, tx.DistributedTxId);
}
internal override void ChangeStatePromotedAborted( InternalTransaction tx )
{
_TransactionStatePromotedAborted.EnterState( tx );
}
internal override void ChangeStateTransactionAborted( InternalTransaction tx, Exception e )
{
// Don't do this yet wait until all of the notifications come back.
}
internal override void RestartCommitIfNeeded( InternalTransaction tx )
{
// Commit cannot be restarted
}
}
// TransactionStatePromotedP0Aborting
//
// This state indicates that the transaction has been promoted but aborted by a phase 0 volatile
// enlistment. Once the volatile enlistments have finished responding the tx can be finished.
internal class TransactionStatePromotedP0Aborting : TransactionStatePromotedAborting
{
internal override void EnterState( InternalTransaction tx )
{
CommonEnterState( tx );
ChangeStatePromotedAborted( tx );
// If we have a volatilePreparingEnlistment tell it to roll back.
if ( tx.phase0Volatiles.VolatileDemux.preparingEnlistment != null )
{
System.Threading.Monitor.Exit( tx );
try
{
// Tell the distributed TM that the tx aborted.
tx.phase0Volatiles.VolatileDemux.oletxEnlistment.ForceRollback();
}
finally
{
#pragma warning disable 0618
//@
System.Threading.Monitor.Enter(tx);
#pragma warning restore 0618
}
}
else
{
// Otherwise make sure that the transaction rolls back.
tx.PromotedTransaction.Rollback();
}
}
internal override void Phase0VolatilePrepareDone( InternalTransaction tx )
{
// If this happens as a ---- it is just fine.
}
}
// TransactionStatePromotedP1Aborting
//
// This state indicates that the transaction has been promoted but aborted by a phase 1 volatile
// enlistment. Once the volatile enlistments have finished responding the tx can be finished.
internal class TransactionStatePromotedP1Aborting : TransactionStatePromotedAborting
{
internal override void EnterState( InternalTransaction tx )
{
CommonEnterState( tx );
Debug.Assert( tx.phase1Volatiles.VolatileDemux != null, "Volatile Demux must exist." );
ChangeStatePromotedAborted( tx );
System.Threading.Monitor.Exit( tx );
try
{
// Tell the distributed TM that the tx aborted.
tx.phase1Volatiles.VolatileDemux.oletxEnlistment.ForceRollback();
}
finally
{
#pragma warning disable 0618
//@
System.Threading.Monitor.Enter(tx);
#pragma warning restore 0618
}
}
internal override void Phase1VolatilePrepareDone( InternalTransaction tx )
{
// If this happens as a ---- it is fine.
}
}
// TransactionStatePromotedEnded
//
// This is a common base class for committed, aborted, and indoubt states of a promoted
// transaction.
internal abstract class TransactionStatePromotedEnded : TransactionStateEnded
{
internal override void EnterState( InternalTransaction tx )
{
base.EnterState( tx );
CommonEnterState( tx );
if ( !ThreadPool.QueueUserWorkItem( SignalMethod, tx ))
{
throw TransactionException.CreateInvalidOperationException(
SR.GetString( SR.TraceSourceLtm ),
SR.GetString(SR.UnexpectedFailureOfThreadPool),
null,
tx == null ? Guid.Empty : tx.DistributedTxId
);
}
}
internal override void AddOutcomeRegistrant( InternalTransaction tx, TransactionCompletedEventHandler transactionCompletedDelegate )
{
if ( transactionCompletedDelegate != null )
{
TransactionEventArgs args = new TransactionEventArgs( );
args.transaction = tx.outcomeSource.InternalClone();
transactionCompletedDelegate( args.transaction, args );
}
}
internal override void EndCommit( InternalTransaction tx )
{
// Test the outcome of the transaction and respond accordingly.
Debug.Assert( tx.PromotedTransaction != null, "Promoted state not valid for transaction." );
PromotedTransactionOutcome( tx );
}
internal override void CompleteBlockingClone( InternalTransaction tx )
{
// The transaction is finished ignore these.
}
internal override void CompleteAbortingClone( InternalTransaction tx )
{
// The transaction is finished ignore these.
}
internal override void CreateBlockingClone( InternalTransaction tx )
{
throw TransactionException.CreateTransactionStateException( SR.GetString( SR.TraceSourceLtm ), tx.innerException, tx.DistributedTxId);
}
internal override void CreateAbortingClone( InternalTransaction tx )
{
throw TransactionException.CreateTransactionStateException( SR.GetString( SR.TraceSourceLtm ), tx.innerException, tx.DistributedTxId);
}
internal override Guid get_Identifier( InternalTransaction tx )
{
return tx.PromotedTransaction.Identifier;
}
internal override void Promote( InternalTransaction tx )
{
// do nothing, we are already promoted
}
protected abstract void PromotedTransactionOutcome( InternalTransaction tx );
// Double-checked locking pattern requires volatile for read/write synchronization
private static volatile WaitCallback signalMethod;
private static WaitCallback SignalMethod
{
get
{
if ( signalMethod == null )
{
lock ( ClassSyncObject )
{
if ( signalMethod == null )
{
signalMethod = new WaitCallback( SignalCallback );
}
}
}
return signalMethod;
}
}
private static void SignalCallback( object state )
{
InternalTransaction tx = (InternalTransaction)state;
lock ( tx )
{
tx.SignalAsyncCompletion();
TransactionManager.TransactionTable.Remove( tx );
}
}
}
// TransactionStatePromotedAborted
//
// This state indicates that the transaction has been promoted and the outcome
// of the transaction is aborted.
internal class TransactionStatePromotedAborted : TransactionStatePromotedEnded
{
internal override void EnterState( InternalTransaction tx )
{
base.EnterState( tx );
// Tell all the enlistments the outcome.
if ( tx.phase1Volatiles.VolatileDemux != null )
{
tx.phase1Volatiles.VolatileDemux.BroadcastRollback( ref tx.phase1Volatiles );
}
if ( tx.phase0Volatiles.VolatileDemux != null )
{
tx.phase0Volatiles.VolatileDemux.BroadcastRollback( ref tx.phase0Volatiles );
}
// Fire Completion for anyone listening
tx.FireCompletion( );
// We don't need to do the AsyncCompletion stuff. If it was needed, it was done out of SignalCallback.
if ( DiagnosticTrace.Warning )
{
TransactionAbortedTraceRecord.Trace( SR.GetString( SR.TraceSourceLtm ),
tx.TransactionTraceId
);
}
}
internal override TransactionStatus get_Status( InternalTransaction tx )
{
return TransactionStatus.Aborted;
}
internal override void Rollback( InternalTransaction tx, Exception e )
{
// Already done.
}
internal override void BeginCommit( InternalTransaction tx, bool asyncCommit, AsyncCallback asyncCallback, object asyncState )
{
throw TransactionAbortedException.Create( SR.GetString( SR.TraceSourceLtm), SR.GetString(SR.TransactionAborted), tx.innerException, tx.DistributedTxId);
}
internal override void CreateBlockingClone( InternalTransaction tx )
{
throw TransactionAbortedException.Create(SR.GetString(SR.TraceSourceLtm), SR.GetString(SR.TransactionAborted), tx.innerException, tx.DistributedTxId);
}
internal override void CreateAbortingClone( InternalTransaction tx )
{
throw TransactionAbortedException.Create(SR.GetString(SR.TraceSourceLtm), SR.GetString(SR.TransactionAborted), tx.innerException, tx.DistributedTxId);
}
internal override void RestartCommitIfNeeded( InternalTransaction tx )
{
// Commit cannot be restarted
}
internal override void Phase0VolatilePrepareDone( InternalTransaction tx )
{
// Since the transaction is aborted ignore it.
}
internal override void Phase1VolatilePrepareDone( InternalTransaction tx )
{
// Since the transaction is aborted ignore it.
}
internal override void ChangeStatePromotedPhase0( InternalTransaction tx )
{
throw new TransactionAbortedException(tx.innerException, tx.DistributedTxId);
}
internal override void ChangeStatePromotedPhase1( InternalTransaction tx )
{
throw new TransactionAbortedException(tx.innerException, tx.DistributedTxId);
}
internal override void ChangeStatePromotedAborted( InternalTransaction tx )
{
// This call may come from multiple events. Support being told more than once.
}
internal override void ChangeStateTransactionAborted( InternalTransaction tx, Exception e )
{
// This may come from a promotable single phase enlistments abort response.
}
protected override void PromotedTransactionOutcome( InternalTransaction tx )
{
if ( ( null == tx.innerException ) && ( null != tx.PromotedTransaction ) )
{
tx.innerException = tx.PromotedTransaction.InnerException;
}
throw TransactionAbortedException.Create(SR.GetString(SR.TraceSourceLtm), SR.GetString(SR.TransactionAborted), tx.innerException, tx.DistributedTxId);
}
internal override void CheckForFinishedTransaction( InternalTransaction tx )
{
throw new TransactionAbortedException(tx.innerException, tx.DistributedTxId);
}
internal override void GetObjectData( InternalTransaction tx, SerializationInfo serializationInfo, StreamingContext context )
{
throw TransactionAbortedException.Create(SR.GetString(SR.TraceSourceLtm), SR.GetString(SR.TransactionAborted), tx.innerException, tx.DistributedTxId);
}
internal override void InDoubtFromDtc( InternalTransaction tx )
{
// Getting this event would mean that a PSPE enlistment has told us the
// transaction outcome. It is possible that a PSPE enlistment would know
// the transaction outcome when DTC does not. So ignore the indoubt
// notification from DTC.
}
internal override void InDoubtFromEnlistment( InternalTransaction tx )
{
// In this case DTC has told us the outcome but a PSPE enlistment
// is telling us that it does not know the outcome of the transaction.
// So ignore the notification from the enlistment.
}
}
// TransactionStatePromotedCommitted
//
// This state indicates that the transaction has been promoted and the outcome
// of the transaction is committed
internal class TransactionStatePromotedCommitted : TransactionStatePromotedEnded
{
internal override void EnterState( InternalTransaction tx )
{
base.EnterState( tx );
// Tell all the enlistments the outcome.
if ( tx.phase1Volatiles.VolatileDemux != null )
{
tx.phase1Volatiles.VolatileDemux.BroadcastCommitted( ref tx.phase1Volatiles );
}
if ( tx.phase0Volatiles.VolatileDemux != null )
{
tx.phase0Volatiles.VolatileDemux.BroadcastCommitted( ref tx.phase0Volatiles );
}
// Fire Completion for anyone listening
tx.FireCompletion( );
// We don't need to do the AsyncCompletion stuff. If it was needed, it was done out of SignalCallback.
if ( DiagnosticTrace.Verbose )
{
TransactionCommittedTraceRecord.Trace( SR.GetString( SR.TraceSourceLtm ),
tx.TransactionTraceId
);
}
}
internal override TransactionStatus get_Status( InternalTransaction tx )
{
return TransactionStatus.Committed;
}
internal override void ChangeStatePromotedCommitted( InternalTransaction tx )
{
// This call may come from multiple different events. Support being told more than once.
}
protected override void PromotedTransactionOutcome( InternalTransaction tx )
{
// This is a happy transaction.
}
internal override void InDoubtFromDtc( InternalTransaction tx )
{
// Getting this event would mean that a PSPE enlistment has told us the
// transaction outcome. It is possible that a PSPE enlistment would know
// the transaction outcome when DTC does not. So ignore the indoubt
// notification from DTC.
}
internal override void InDoubtFromEnlistment( InternalTransaction tx )
{
// In this case DTC has told us the outcome but a PSPE enlistment
// is telling us that it does not know the outcome of the transaction.
// So ignore the notification from the enlistment.
}
}
// TransactionStatePromotedIndoubt
//
// This state indicates that the transaction has been promoted but the outcome
// of the transaction is indoubt.
internal class TransactionStatePromotedIndoubt : TransactionStatePromotedEnded
{
internal override void EnterState( InternalTransaction tx )
{
base.EnterState( tx );
// Tell all the enlistments the outcome.
if ( tx.phase1Volatiles.VolatileDemux != null )
{
tx.phase1Volatiles.VolatileDemux.BroadcastInDoubt( ref tx.phase1Volatiles );
}
if ( tx.phase0Volatiles.VolatileDemux != null )
{
tx.phase0Volatiles.VolatileDemux.BroadcastInDoubt( ref tx.phase0Volatiles );
}
// Fire Completion for anyone listening
tx.FireCompletion( );
// We don't need to do the AsyncCompletion stuff. If it was needed, it was done out of SignalCallback.
if ( DiagnosticTrace.Warning )
{
TransactionInDoubtTraceRecord.Trace( SR.GetString( SR.TraceSourceLtm ),
tx.TransactionTraceId
);
}
}
internal override TransactionStatus get_Status( InternalTransaction tx )
{
return TransactionStatus.InDoubt;
}
internal override void RestartCommitIfNeeded( InternalTransaction tx )
{
// Commit cannot be restarted
}
internal override void ChangeStatePromotedPhase0( InternalTransaction tx )
{
throw TransactionInDoubtException.Create(SR.GetString(SR.TraceSourceBase), SR.GetString(SR.TransactionIndoubt), tx.innerException, tx.DistributedTxId);
}
internal override void ChangeStatePromotedPhase1( InternalTransaction tx )
{
throw TransactionInDoubtException.Create(SR.GetString(SR.TraceSourceBase), SR.GetString(SR.TransactionIndoubt), tx.innerException, tx.DistributedTxId);
}
internal override void InDoubtFromDtc( InternalTransaction tx )
{
// This call may actually come from multiple sources that ----.
// Since we already took action based on the first notification ignore the
// others.
}
internal override void InDoubtFromEnlistment( InternalTransaction tx )
{
// This call may actually come from multiple sources that ----.
// Since we already took action based on the first notification ignore the
// others.
}
protected override void PromotedTransactionOutcome( InternalTransaction tx )
{
if ( ( null == tx.innerException ) && ( null != tx.PromotedTransaction ) )
{
tx.innerException = tx.PromotedTransaction.InnerException;
}
throw TransactionInDoubtException.Create(SR.GetString(SR.TraceSourceBase), SR.GetString(SR.TransactionIndoubt), tx.innerException, tx.DistributedTxId);
}
internal override void CheckForFinishedTransaction( InternalTransaction tx )
{
throw TransactionInDoubtException.Create(SR.GetString(SR.TraceSourceBase), SR.GetString(SR.TransactionIndoubt), tx.innerException, tx.DistributedTxId);
}
internal override void GetObjectData( InternalTransaction tx, SerializationInfo serializationInfo, StreamingContext context )
{
throw TransactionInDoubtException.Create(SR.GetString(SR.TraceSourceBase), SR.GetString(SR.TransactionIndoubt), tx.innerException, tx.DistributedTxId);
}
internal override void ChangeStatePromotedAborted( InternalTransaction tx )
{
// Transaction outcome can come from different directions. In the case of InDoubt
// transactions it is possible that one source knowns the actual outcome for
// the transaction. However since the transaction does not know if it will receive
// a different answer for the outcome it accepts the first answer it gets.
// By the time we receive a better answer the clients of this transaction
// have already been informed that the transaction is InDoubt.
}
internal override void ChangeStatePromotedCommitted( InternalTransaction tx )
{
// See comment in ChangeStatePromotedAborted
}
}
// TransactionStateDelegatedBase
//
// This state is the base state for delegated transactions
internal abstract class TransactionStateDelegatedBase : TransactionStatePromoted
{
internal override void EnterState( InternalTransaction tx )
{
if ( tx.outcomeSource.isoLevel == IsolationLevel.Snapshot )
{
throw TransactionException.CreateInvalidOperationException( SR.GetString( SR.TraceSourceLtm ),
SR.GetString(SR.CannotPromoteSnapshot), null, tx == null ? Guid.Empty : tx.DistributedTxId);
}
// Assign the state
CommonEnterState( tx );
// Create a transaction with the distributed transaction manager
Oletx.OletxTransaction distributedTx = null;
try
{
// Ask the delegation interface to promote the transaction.
if ( DiagnosticTrace.Verbose && tx.durableEnlistment != null )
{
EnlistmentNotificationCallTraceRecord.Trace( SR.GetString( SR.TraceSourceLtm ),
tx.durableEnlistment.EnlistmentTraceId,
NotificationCall.Promote
);
}
distributedTx = _TransactionStatePSPEOperation.PSPEPromote( tx );
}
catch ( TransactionPromotionException e )
{
tx.innerException = e;
if ( DiagnosticTrace.Verbose )
{
ExceptionConsumedTraceRecord.Trace( SR.GetString( SR.TraceSourceLtm ),
e );
}
}
finally
{
if ( ((object)distributedTx) == null )
{
// There was an exception trying to create the distributed transaction abort
// the local transaction and exit.
tx.State.ChangeStateAbortedDuringPromotion( tx );
}
}
if ( ((object)distributedTx) == null )
{
return;
}
// If tx.PromotedTransaction is already set to the distributedTx that was
// returned, then the PSPE enlistment must have used
// Transaction.PSPEPromoteAndConvertToEnlistDurable to promote the transaction
// within the same AppDomain. So we don't need to add the distributedTx to the
// PromotedTransactionTable and we don't need to call
// FireDistributedTransactionStarted and we don't need to promote the
// enlistments. That was all done when the transaction was changed to
// TransactionStatePromoted.
if (tx.PromotedTransaction != distributedTx)
{
// Associate the distributed transaction with the local transaction.
tx.PromotedTransaction = distributedTx;
// Add a weak reference to the transaction to the promotedTransactionTable.
Hashtable promotedTransactionTable = TransactionManager.PromotedTransactionTable;
lock (promotedTransactionTable)
{
// Since we are adding this reference to the table create an object that will clean that
// entry up.
tx.finalizedObject = new FinalizedObject(tx, tx.PromotedTransaction.Identifier);
WeakReference weakRef = new WeakReference(tx.outcomeSource, false);
promotedTransactionTable[tx.PromotedTransaction.Identifier] = weakRef;
}
TransactionManager.FireDistributedTransactionStarted(tx.outcomeSource);
if (DiagnosticTrace.Information)
{
TransactionPromotedTraceRecord.Trace(SR.GetString(SR.TraceSourceLtm),
tx.TransactionTraceId,
distributedTx.TransactionTraceId
);
}
// Once we have a promoted transaction promote the enlistments.
PromoteEnlistmentsAndOutcome(tx);
}
}
}
// TransactionStateDelegated
//
// This state represents a transaction that had a promotable single phase enlistment that then
// was promoted. Most of the functionality is inherited from transaction state promoted
// except for the way that commit happens.
internal class TransactionStateDelegated : TransactionStateDelegatedBase
{
internal override void BeginCommit( InternalTransaction tx, bool asyncCommit, AsyncCallback asyncCallback, object asyncState )
{
// Store the given values
tx.asyncCommit = asyncCommit;
tx.asyncCallback = asyncCallback;
tx.asyncState = asyncState;
// Initiate the commit process.
_TransactionStateDelegatedCommitting.EnterState( tx );
}
internal override bool PromoteDurable( InternalTransaction tx )
{
// Let the enlistment know that it has been delegated. For this type of enlistment that
// is really all that needs to be done.
tx.durableEnlistment.State.ChangeStateDelegated( tx.durableEnlistment );
return true;
}
internal override void RestartCommitIfNeeded( InternalTransaction tx )
{
_TransactionStateDelegatedP0Wave.EnterState( tx );
}
internal override void Rollback( InternalTransaction tx, Exception e )
{
// Pass the Rollback through the promotable single phase enlistment to be
// certain it is notified.
if ( tx.innerException == null )
{
tx.innerException = e;
}
_TransactionStateDelegatedAborting.EnterState( tx );
}
}
// TransactionStatePromotedNonMSDTCBase
//
// This is the base class for non-MSDTC promoted states. It's main function is to pass calls
// through to the distributed transaction.
internal abstract class TransactionStatePromotedNonMSDTCBase : TransactionState
{
internal override TransactionStatus get_Status(InternalTransaction tx)
{
return TransactionStatus.Active;
}
internal override Enlistment EnlistVolatile(
InternalTransaction tx,
IEnlistmentNotification enlistmentNotification,
EnlistmentOptions enlistmentOptions,
Transaction atomicTransaction
)
{
Enlistment enlistment = new Enlistment(tx, enlistmentNotification, null, atomicTransaction, enlistmentOptions);
if ((enlistmentOptions & EnlistmentOptions.EnlistDuringPrepareRequired) != 0)
{
AddVolatileEnlistment(ref tx.phase0Volatiles, enlistment);
}
else
{
AddVolatileEnlistment(ref tx.phase1Volatiles, enlistment);
}
if (DiagnosticTrace.Information)
{
EnlistmentTraceRecord.Trace(SR.GetString(SR.TraceSourceLtm),
enlistment.InternalEnlistment.EnlistmentTraceId,
EnlistmentType.Volatile,
enlistmentOptions
);
}
return enlistment;
}
internal override Enlistment EnlistVolatile(
InternalTransaction tx,
ISinglePhaseNotification enlistmentNotification,
EnlistmentOptions enlistmentOptions,
Transaction atomicTransaction
)
{
Enlistment enlistment = new Enlistment(tx, enlistmentNotification, enlistmentNotification, atomicTransaction, enlistmentOptions);
if ((enlistmentOptions & EnlistmentOptions.EnlistDuringPrepareRequired) != 0)
{
AddVolatileEnlistment(ref tx.phase0Volatiles, enlistment);
}
else
{
AddVolatileEnlistment(ref tx.phase1Volatiles, enlistment);
}
if (DiagnosticTrace.Information)
{
EnlistmentTraceRecord.Trace(SR.GetString(SR.TraceSourceLtm),
enlistment.InternalEnlistment.EnlistmentTraceId,
EnlistmentType.Volatile,
enlistmentOptions
);
}
return enlistment;
}
internal override Enlistment EnlistDurable(
InternalTransaction tx,
Guid resourceManagerIdentifier,
IEnlistmentNotification enlistmentNotification,
EnlistmentOptions enlistmentOptions,
Transaction atomicTransaction
)
{
throw new TransactionPromotionException(string.Format(CultureInfo.CurrentCulture,
SR.GetString(SR.PromoterTypeUnrecognized), tx.promoterType.ToString()),
tx.innerException);
}
internal override Enlistment EnlistDurable(
InternalTransaction tx,
Guid resourceManagerIdentifier,
ISinglePhaseNotification enlistmentNotification,
EnlistmentOptions enlistmentOptions,
Transaction atomicTransaction
)
{
throw new TransactionPromotionException(string.Format(CultureInfo.CurrentCulture,
SR.GetString(SR.PromoterTypeUnrecognized), tx.promoterType.ToString()),
tx.innerException);
}
internal override bool EnlistPromotableSinglePhase(
InternalTransaction tx, IPromotableSinglePhaseNotification promotableSinglePhaseNotification,
Transaction atomicTransaction,
Guid promoterType
)
{
// The transaction has been promoted and cannot support a promotable singe phase enlistment
return false;
}
internal override void Rollback(InternalTransaction tx, Exception e)
{
// Start the process for abort. Transitioning to the Aborted state will cause
// the tx.durableEnlistment to get aborted, which is how the non-MSDTC
// transaction promoter will get notified of the abort.
Debug.Assert(tx.durableEnlistment != null, "PromotedNonMSDTC state is not valid for transaction");
if (tx.innerException == null)
{
tx.innerException = e;
}
_TransactionStateAborted.EnterState(tx);
}
internal override Guid get_Identifier(InternalTransaction tx)
{
// In this state, we know that the we are dealing with a non-MSDTC promoter, so get the identifier from the internal transaction.
return tx.distributedTransactionIdentifierNonMSDTC;
}
internal override void AddOutcomeRegistrant(InternalTransaction tx, TransactionCompletedEventHandler transactionCompletedDelegate)
{
// Add this guy to the list of people to be notified of the outcome.
tx.transactionCompletedDelegate = (TransactionCompletedEventHandler)
System.Delegate.Combine(tx.transactionCompletedDelegate, transactionCompletedDelegate);
}
// Start the commit processing by transitioning to TransactionStatePromotedNonMSDTCPhase0.
internal override void BeginCommit(InternalTransaction tx, bool asyncCommit, AsyncCallback asyncCallback, object asyncState)
{
tx.asyncCommit = asyncCommit;
tx.asyncCallback = asyncCallback;
tx.asyncState = asyncState;
_TransactionStatePromotedNonMSDTCPhase0.EnterState(tx);
}
internal override void CompleteBlockingClone(InternalTransaction tx)
{
// First try to complete one of the internal blocking clones
if (tx.phase0Volatiles.dependentClones > 0)
{
// decrement the number of clones
tx.phase0Volatiles.dependentClones--;
// Make certain we increment the right list.
Debug.Assert(tx.phase0Volatiles.preparedVolatileEnlistments <=
tx.phase0Volatiles.volatileEnlistmentCount + tx.phase0Volatiles.dependentClones);
// Check to see if all of the volatile enlistments are done.
if (tx.phase0Volatiles.preparedVolatileEnlistments ==
tx.phase0VolatileWaveCount + tx.phase0Volatiles.dependentClones)
{
tx.State.Phase0VolatilePrepareDone(tx);
}
}
}
internal override void CompleteAbortingClone(InternalTransaction tx)
{
// A blocking clone simulates a phase 1 volatile
//
// Unlike a blocking clone however the aborting clones need to be accounted
// for specifically. So when one is complete remove it from the list.
tx.phase1Volatiles.dependentClones--;
Debug.Assert(tx.phase1Volatiles.dependentClones >= 0);
}
internal override void CreateBlockingClone(InternalTransaction tx)
{
// A blocking clone simulates a phase 0 volatile
tx.phase0Volatiles.dependentClones++;
}
internal override void CreateAbortingClone(InternalTransaction tx)
{
// An aborting clone simulates a phase 1 volatile
tx.phase1Volatiles.dependentClones++;
}
internal override bool ContinuePhase0Prepares()
{
return true;
}
internal override void GetObjectData(InternalTransaction tx, SerializationInfo serializationInfo, StreamingContext context)
{
throw new TransactionPromotionException(string.Format(CultureInfo.CurrentCulture,
SR.GetString(SR.PromoterTypeUnrecognized), tx.promoterType.ToString()),
tx.innerException);
}
internal override void ChangeStateTransactionAborted(InternalTransaction tx, Exception e)
{
// Just transition to Aborted. The PSPE will be told to rollback thru the durableEnlistment.
// This is also overridden in TransactionStatePromotedNonMSDTCSinglePhaseCommit
// that does something slightly differently.
if (tx.innerException == null)
{
tx.innerException = e;
}
_TransactionStateAborted.EnterState(tx);
}
internal override void InDoubtFromEnlistment(InternalTransaction tx)
{
_TransactionStatePromotedNonMSDTCIndoubt.EnterState(tx);
}
internal override void ChangeStateAbortedDuringPromotion(InternalTransaction tx)
{
_TransactionStateAborted.EnterState(tx);
}
internal override void Timeout(InternalTransaction tx)
{
if (DiagnosticTrace.Warning)
{
TransactionTimeoutTraceRecord.Trace(SR.GetString(SR.TraceSourceLtm),
tx.TransactionTraceId
);
}
TimeoutException e = new TimeoutException(SR.GetString(SR.TraceTransactionTimeout));
this.Rollback(tx, e);
}
internal override void Promote(InternalTransaction tx)
{
// do nothing, we are already promoted
}
internal override void Phase0VolatilePrepareDone(InternalTransaction tx)
{
// Early done notifications may come from volatiles at any time.
// The state machine will handle all enlistments being complete in later phases.
}
internal override void Phase1VolatilePrepareDone(InternalTransaction tx)
{
// Early done notifications may come from volatiles at any time.
// The state machine will handle all enlistments being complete in later phases.
}
internal override byte[] PromotedToken(InternalTransaction tx)
{
// Since we are in TransactionStateDelegatedNonMSDTCBase or one if its derived classes, we
// must already be promoted. So return the InternalTransaction's promotedToken.
Debug.Assert(tx.promotedToken != null, "InternalTransaction.promotedToken is null in TransactionStateDelegatedNonMSDTCBase or one of its derived classes.");
return tx.promotedToken;
}
internal override void DisposeRoot(InternalTransaction tx)
{
tx.State.Rollback(tx, null);
}
}
// TransactionStatePromotedNonMSDTCPhase0
//
// A transaction that is in the beginning stage of committing.
internal class TransactionStatePromotedNonMSDTCPhase0 : TransactionStatePromotedNonMSDTCBase
{
internal override void EnterState(InternalTransaction tx)
{
// Set the transaction state
CommonEnterState(tx);
// Get a copy of the current volatile enlistment count before entering this loop so that other
// threads don't affect the operation of this loop.
int volatileCount = tx.phase0Volatiles.volatileEnlistmentCount;
int dependentCount = tx.phase0Volatiles.dependentClones;
// Store the number of phase0 volatiles for this wave.
tx.phase0VolatileWaveCount = volatileCount;
// Check for volatile enlistments
if (tx.phase0Volatiles.preparedVolatileEnlistments < volatileCount + dependentCount)
{
// Broadcast prepare to the phase 0 enlistments
for (int i = 0; i < volatileCount; i++)
{
tx.phase0Volatiles.volatileEnlistments[i].twoPhaseState.ChangeStatePreparing(tx.phase0Volatiles.volatileEnlistments[i]);
if (!tx.State.ContinuePhase0Prepares())
{
break;
}
}
}
else
{
// No volatile enlistments. Start phase 1.
_TransactionStatePromotedNonMSDTCVolatilePhase1.EnterState(tx);
}
}
internal override void BeginCommit(InternalTransaction tx, bool asyncCommit, AsyncCallback asyncCallback, object asyncState)
{
throw TransactionException.CreateTransactionStateException(SR.GetString(SR.TraceSourceLtm), tx.innerException, tx.DistributedTxId);
}
internal override void Rollback(InternalTransaction tx, Exception e)
{
ChangeStateTransactionAborted(tx, e);
}
// Volatile prepare is done for Phase0 enlistments
internal override void Phase0VolatilePrepareDone(InternalTransaction tx)
{
// Check to see if any Phase0Volatiles have been added in Phase0.
// If so go through the list again.
// Get a copy of the current volatile enlistment count before entering this loop so that other
// threads don't affect the operation of this loop.
int volatileCount = tx.phase0Volatiles.volatileEnlistmentCount;
int dependentCount = tx.phase0Volatiles.dependentClones;
// Store the number of phase0 volatiles for this wave.
tx.phase0VolatileWaveCount = volatileCount;
// Check for volatile enlistments
if (tx.phase0Volatiles.preparedVolatileEnlistments < volatileCount + dependentCount)
{
// Broadcast prepare to the phase 0 enlistments
for (int i = 0; i < volatileCount; i++)
{
tx.phase0Volatiles.volatileEnlistments[i].twoPhaseState.ChangeStatePreparing(tx.phase0Volatiles.volatileEnlistments[i]);
if (!tx.State.ContinuePhase0Prepares())
{
break;
}
}
}
else
{
// No volatile enlistments. Start phase 1.
_TransactionStatePromotedNonMSDTCVolatilePhase1.EnterState(tx);
}
}
internal override void Phase1VolatilePrepareDone(InternalTransaction tx)
{
// Ignore this for now it can be checked again in Phase 1
}
internal override bool ContinuePhase0Prepares()
{
return true;
}
}
// TransactionStatePromotedNonMSDTCVolatilePhase1
//
// Represents the transaction state during phase 1 preparing volatile enlistments
internal class TransactionStatePromotedNonMSDTCVolatilePhase1 : TransactionStatePromotedNonMSDTCBase
{
internal override void EnterState(InternalTransaction tx)
{
// Set the transaction state
CommonEnterState(tx);
// Mark the committable transaction as complete.
tx.committableTransaction.complete = true;
// If at this point there are phase1 dependent clones abort the transaction
if (tx.phase1Volatiles.dependentClones != 0)
{
ChangeStateTransactionAborted(tx, null);
return;
}
if (tx.phase1Volatiles.volatileEnlistmentCount > 0)
{
// Broadcast prepare to the phase 0 enlistments
for (int i = 0; i < tx.phase1Volatiles.volatileEnlistmentCount; i++)
{
tx.phase1Volatiles.volatileEnlistments[i].twoPhaseState.ChangeStatePreparing(tx.phase1Volatiles.volatileEnlistments[i]);
if (!tx.State.ContinuePhase1Prepares())
{
break;
}
}
}
else
{
// No volatile phase 1 enlistments. Transition to the state that will do SinglePhaseCommit to the PSPE.
_TransactionStatePromotedNonMSDTCSinglePhaseCommit.EnterState(tx);
}
}
internal override void BeginCommit(InternalTransaction tx, bool asyncCommit, AsyncCallback asyncCallback, object asyncState)
{
throw TransactionException.CreateTransactionStateException(SR.GetString(SR.TraceSourceLtm), tx.innerException, tx.DistributedTxId);
}
internal override void Rollback(InternalTransaction tx, Exception e)
{
ChangeStateTransactionAborted(tx, e);
}
// Volatile prepare is done for Phase1
internal override void Phase1VolatilePrepareDone(InternalTransaction tx)
{
_TransactionStatePromotedNonMSDTCSinglePhaseCommit.EnterState(tx);
}
internal override bool ContinuePhase1Prepares()
{
return true;
}
internal override Enlistment EnlistVolatile(
InternalTransaction tx,
IEnlistmentNotification enlistmentNotification,
EnlistmentOptions enlistmentOptions,
Transaction atomicTransaction
)
{
throw TransactionException.Create(SR.GetString(SR.TooLate), tx == null ? Guid.Empty : tx.DistributedTxId);
}
internal override Enlistment EnlistVolatile(
InternalTransaction tx,
ISinglePhaseNotification enlistmentNotification,
EnlistmentOptions enlistmentOptions,
Transaction atomicTransaction
)
{
throw TransactionException.Create(SR.GetString(SR.TooLate), tx == null ? Guid.Empty : tx.DistributedTxId);
}
internal override bool EnlistPromotableSinglePhase(
InternalTransaction tx, IPromotableSinglePhaseNotification promotableSinglePhaseNotification,
Transaction atomicTransaction,
Guid promoterType
)
{
throw TransactionException.Create(SR.GetString(SR.TooLate), tx == null ? Guid.Empty : tx.DistributedTxId);
}
internal override void CreateBlockingClone(InternalTransaction tx)
{
throw TransactionException.Create(SR.GetString(SR.TooLate), tx == null ? Guid.Empty : tx.DistributedTxId);
}
internal override void CreateAbortingClone(InternalTransaction tx)
{
throw TransactionException.Create(SR.GetString(SR.TooLate), tx == null ? Guid.Empty : tx.DistributedTxId);
}
}
// TransactionStatePromotedNonMSDTCSinglePhaseCommit
//
// The transaction has been delegated to a NON-MSDTC promoter and is in the process of committing.
internal class TransactionStatePromotedNonMSDTCSinglePhaseCommit : TransactionStatePromotedNonMSDTCBase
{
internal override void EnterState(InternalTransaction tx)
{
CommonEnterState(tx);
if (DiagnosticTrace.Verbose)
{
EnlistmentNotificationCallTraceRecord.Trace(SR.GetString(SR.TraceSourceLtm),
tx.durableEnlistment.EnlistmentTraceId,
NotificationCall.SinglePhaseCommit
);
}
// We are about to tell the PSPE to do the SinglePhaseCommit. It is too late for us to timeout the transaction.
// Remove this from the timeout list
TransactionManager.TransactionTable.Remove(tx);
tx.durableEnlistment.State.ChangeStateCommitting(tx.durableEnlistment);
}
internal override void BeginCommit(InternalTransaction tx, bool asyncCommit, AsyncCallback asyncCallback, object asyncState)
{
throw TransactionException.CreateTransactionStateException(SR.GetString(SR.TraceSourceLtm), tx.innerException, tx.DistributedTxId);
}
internal override void Rollback(InternalTransaction tx, Exception e)
{
// We have told the PSPE enlistment to do a single phase commit. It's too late to rollback.
throw TransactionException.CreateTransactionStateException(SR.GetString(SR.TraceSourceLtm), tx.innerException, tx.DistributedTxId);
}
internal override void ChangeStateTransactionCommitted(InternalTransaction tx)
{
// The durable enlistment must have committed. Go to the committed state.
_TransactionStatePromotedNonMSDTCCommitted.EnterState(tx);
}
internal override void InDoubtFromEnlistment(InternalTransaction tx)
{
// The transaction is indoubt
_TransactionStatePromotedNonMSDTCIndoubt.EnterState(tx);
}
internal override void ChangeStateTransactionAborted(InternalTransaction tx, Exception e)
{
if (tx.innerException == null)
{
tx.innerException = e;
}
// The durable enlistment must have aborted. Go to the aborted state.
_TransactionStatePromotedNonMSDTCAborted.EnterState(tx);
}
internal override void ChangeStateAbortedDuringPromotion(InternalTransaction tx)
{
_TransactionStateAborted.EnterState(tx);
}
internal override Enlistment EnlistVolatile(
InternalTransaction tx,
IEnlistmentNotification enlistmentNotification,
EnlistmentOptions enlistmentOptions,
Transaction atomicTransaction
)
{
throw TransactionException.Create(SR.GetString(SR.TooLate), tx == null ? Guid.Empty : tx.DistributedTxId);
}
internal override Enlistment EnlistVolatile(
InternalTransaction tx,
ISinglePhaseNotification enlistmentNotification,
EnlistmentOptions enlistmentOptions,
Transaction atomicTransaction
)
{
throw TransactionException.Create(SR.GetString(SR.TooLate), tx == null ? Guid.Empty : tx.DistributedTxId);
}
internal override bool EnlistPromotableSinglePhase(
InternalTransaction tx, IPromotableSinglePhaseNotification promotableSinglePhaseNotification,
Transaction atomicTransaction,
Guid promoterType
)
{
throw TransactionException.Create(SR.GetString(SR.TooLate), tx == null ? Guid.Empty : tx.DistributedTxId);
}
internal override void CreateBlockingClone(InternalTransaction tx)
{
throw TransactionException.Create(SR.GetString(SR.TooLate), tx == null ? Guid.Empty : tx.DistributedTxId);
}
internal override void CreateAbortingClone(InternalTransaction tx)
{
throw TransactionException.Create(SR.GetString(SR.TooLate), tx == null ? Guid.Empty : tx.DistributedTxId);
}
}
// TransactionStatePromotedNonMSDTCEnded
//
// This is a common base class for committed, aborted, and indoubt states of a non-MSDTC promoted
// transaction.
internal abstract class TransactionStatePromotedNonMSDTCEnded : TransactionStateEnded
{
internal override void EnterState(InternalTransaction tx)
{
base.EnterState(tx);
CommonEnterState(tx);
if (!ThreadPool.QueueUserWorkItem(SignalMethod, tx))
{
throw TransactionException.CreateInvalidOperationException(
SR.GetString(SR.TraceSourceLtm),
SR.GetString(SR.UnexpectedFailureOfThreadPool),
null,
tx == null ? Guid.Empty : tx.DistributedTxId
);
}
}
internal override void AddOutcomeRegistrant(InternalTransaction tx, TransactionCompletedEventHandler transactionCompletedDelegate)
{
if (transactionCompletedDelegate != null)
{
TransactionEventArgs args = new TransactionEventArgs();
args.transaction = tx.outcomeSource.InternalClone();
transactionCompletedDelegate(args.transaction, args);
}
}
internal override void EndCommit(InternalTransaction tx)
{
// Test the outcome of the transaction and respond accordingly.
PromotedTransactionOutcome(tx);
}
internal override void CompleteBlockingClone(InternalTransaction tx)
{
// The transaction is finished ignore these.
}
internal override void CompleteAbortingClone(InternalTransaction tx)
{
// The transaction is finished ignore these.
}
internal override void CreateBlockingClone(InternalTransaction tx)
{
throw TransactionException.CreateTransactionStateException(SR.GetString(SR.TraceSourceLtm), tx.innerException, tx.DistributedTxId);
}
internal override void CreateAbortingClone(InternalTransaction tx)
{
throw TransactionException.CreateTransactionStateException(SR.GetString(SR.TraceSourceLtm), tx.innerException, tx.DistributedTxId);
}
internal override Guid get_Identifier(InternalTransaction tx)
{
// In this state, we know that the we are dealing with a non-MSDTC promoter, so get the identifier from the internal transaction.
return tx.distributedTransactionIdentifierNonMSDTC;
}
internal override void Promote(InternalTransaction tx)
{
// do nothing, we are already promoted
}
protected abstract void PromotedTransactionOutcome(InternalTransaction tx);
// Double-checked locking pattern requires volatile for read/write synchronization
private static volatile WaitCallback signalMethod;
private static WaitCallback SignalMethod
{
get
{
if (signalMethod == null)
{
lock (ClassSyncObject)
{
if (signalMethod == null)
{
signalMethod = new WaitCallback(SignalCallback);
}
}
}
return signalMethod;
}
}
private static void SignalCallback(object state)
{
InternalTransaction tx = (InternalTransaction)state;
lock (tx)
{
tx.SignalAsyncCompletion();
}
}
}
// TransactionStatePromotedNonMSDTCAborted
//
// This state indicates that the transaction has been promoted to a non-MSDTC promoter and the outcome
// of the transaction is aborted.
internal class TransactionStatePromotedNonMSDTCAborted : TransactionStatePromotedNonMSDTCEnded
{
internal override void EnterState(InternalTransaction tx)
{
base.EnterState(tx);
// Notify the enlistments that the transaction has aborted
for (int i = 0; i < tx.phase0Volatiles.volatileEnlistmentCount; i++)
{
tx.phase0Volatiles.volatileEnlistments[i].twoPhaseState.InternalAborted(tx.phase0Volatiles.volatileEnlistments[i]);
}
for (int i = 0; i < tx.phase1Volatiles.volatileEnlistmentCount; i++)
{
tx.phase1Volatiles.volatileEnlistments[i].twoPhaseState.InternalAborted(tx.phase1Volatiles.volatileEnlistments[i]);
}
// Notify the durable enlistment
if (tx.durableEnlistment != null)
{
tx.durableEnlistment.State.InternalAborted(tx.durableEnlistment);
}
// Fire Completion for anyone listening
tx.FireCompletion();
// We don't need to do the AsyncCompletion stuff. If it was needed, it was done out of SignalCallback.
if (DiagnosticTrace.Warning)
{
TransactionAbortedTraceRecord.Trace(SR.GetString(SR.TraceSourceLtm),
tx.TransactionTraceId
);
}
}
internal override TransactionStatus get_Status(InternalTransaction tx)
{
return TransactionStatus.Aborted;
}
internal override void Rollback(InternalTransaction tx, Exception e)
{
// Already done.
}
internal override void BeginCommit(InternalTransaction tx, bool asyncCommit, AsyncCallback asyncCallback, object asyncState)
{
throw TransactionAbortedException.Create(SR.GetString(SR.TraceSourceLtm), SR.GetString(SR.TransactionAborted), tx.innerException, tx.DistributedTxId);
}
internal override void CreateBlockingClone(InternalTransaction tx)
{
throw TransactionAbortedException.Create(SR.GetString(SR.TraceSourceLtm), SR.GetString(SR.TransactionAborted), tx.innerException, tx.DistributedTxId);
}
internal override void CreateAbortingClone(InternalTransaction tx)
{
throw TransactionAbortedException.Create(SR.GetString(SR.TraceSourceLtm), SR.GetString(SR.TransactionAborted), tx.innerException, tx.DistributedTxId);
}
internal override void Phase0VolatilePrepareDone(InternalTransaction tx)
{
// Since the transaction is aborted ignore it.
}
internal override void Phase1VolatilePrepareDone(InternalTransaction tx)
{
// Since the transaction is aborted ignore it.
}
internal override void ChangeStateTransactionAborted(InternalTransaction tx, Exception e)
{
// This may come from a promotable single phase enlistments abort response.
}
protected override void PromotedTransactionOutcome(InternalTransaction tx)
{
if ((null == tx.innerException) && (null != tx.PromotedTransaction))
{
tx.innerException = tx.PromotedTransaction.InnerException;
}
throw TransactionAbortedException.Create(SR.GetString(SR.TraceSourceLtm), SR.GetString(SR.TransactionAborted), tx.innerException, tx.DistributedTxId);
}
internal override void CheckForFinishedTransaction(InternalTransaction tx)
{
throw new TransactionAbortedException(tx.innerException, tx.DistributedTxId);
}
internal override void GetObjectData(InternalTransaction tx, SerializationInfo serializationInfo, StreamingContext context)
{
throw TransactionAbortedException.Create(SR.GetString(SR.TraceSourceLtm), SR.GetString(SR.TransactionAborted), tx.innerException, tx.DistributedTxId);
}
}
// TransactionStatePromotedNonMSDTCCommitted
//
// This state indicates that the transaction has been non-MSDTC promoted and the outcome
// of the transaction is committed
internal class TransactionStatePromotedNonMSDTCCommitted : TransactionStatePromotedNonMSDTCEnded
{
internal override void EnterState(InternalTransaction tx)
{
base.EnterState(tx);
// Notify the phase 0 enlistments that the transaction has committed
for (int i = 0; i < tx.phase0Volatiles.volatileEnlistmentCount; i++)
{
tx.phase0Volatiles.volatileEnlistments[i].twoPhaseState.InternalCommitted(tx.phase0Volatiles.volatileEnlistments[i]);
}
// Notify the phase 1 enlistments that the transaction has committed
for (int i = 0; i < tx.phase1Volatiles.volatileEnlistmentCount; i++)
{
tx.phase1Volatiles.volatileEnlistments[i].twoPhaseState.InternalCommitted(tx.phase1Volatiles.volatileEnlistments[i]);
}
// Fire Completion for anyone listening
tx.FireCompletion();
// We don't need to do the AsyncCompletion stuff. If it was needed, it was done out of SignalCallback.
if (DiagnosticTrace.Verbose)
{
TransactionCommittedTraceRecord.Trace(SR.GetString(SR.TraceSourceLtm),
tx.TransactionTraceId
);
}
}
internal override TransactionStatus get_Status(InternalTransaction tx)
{
return TransactionStatus.Committed;
}
protected override void PromotedTransactionOutcome(InternalTransaction tx)
{
// This is a happy transaction.
}
}
// TransactionStatePromotedNonMSDTCIndoubt
//
// This state indicates that the transaction has been non-MSDTC promoted but the outcome
// of the transaction is indoubt.
internal class TransactionStatePromotedNonMSDTCIndoubt : TransactionStatePromotedNonMSDTCEnded
{
internal override void EnterState(InternalTransaction tx)
{
base.EnterState(tx);
// Notify the phase 0 enlistments that the transaction is indoubt
for (int i = 0; i < tx.phase0Volatiles.volatileEnlistmentCount; i++)
{
tx.phase0Volatiles.volatileEnlistments[i].twoPhaseState.InternalIndoubt(tx.phase0Volatiles.volatileEnlistments[i]);
}
// Notify the phase 1 enlistments that the transaction is indoubt
for (int i = 0; i < tx.phase1Volatiles.volatileEnlistmentCount; i++)
{
tx.phase1Volatiles.volatileEnlistments[i].twoPhaseState.InternalIndoubt(tx.phase1Volatiles.volatileEnlistments[i]);
}
// Fire Completion for anyone listening
tx.FireCompletion();
// We don't need to do the AsyncCompletion stuff. If it was needed, it was done out of SignalCallback.
if (DiagnosticTrace.Warning)
{
TransactionInDoubtTraceRecord.Trace(SR.GetString(SR.TraceSourceLtm),
tx.TransactionTraceId
);
}
}
internal override TransactionStatus get_Status(InternalTransaction tx)
{
return TransactionStatus.InDoubt;
}
internal override void ChangeStatePromotedPhase0(InternalTransaction tx)
{
throw TransactionInDoubtException.Create(SR.GetString(SR.TraceSourceBase), SR.GetString(SR.TransactionIndoubt), tx.innerException, tx.DistributedTxId);
}
internal override void ChangeStatePromotedPhase1(InternalTransaction tx)
{
throw TransactionInDoubtException.Create(SR.GetString(SR.TraceSourceBase), SR.GetString(SR.TransactionIndoubt), tx.innerException, tx.DistributedTxId);
}
protected override void PromotedTransactionOutcome(InternalTransaction tx)
{
if ((null == tx.innerException) && (null != tx.PromotedTransaction))
{
tx.innerException = tx.PromotedTransaction.InnerException;
}
throw TransactionInDoubtException.Create(SR.GetString(SR.TraceSourceBase), SR.GetString(SR.TransactionIndoubt), tx.innerException, tx.DistributedTxId);
}
internal override void CheckForFinishedTransaction(InternalTransaction tx)
{
throw TransactionInDoubtException.Create(SR.GetString(SR.TraceSourceBase), SR.GetString(SR.TransactionIndoubt), tx.innerException, tx.DistributedTxId);
}
internal override void GetObjectData(InternalTransaction tx, SerializationInfo serializationInfo, StreamingContext context)
{
throw TransactionInDoubtException.Create(SR.GetString(SR.TraceSourceBase), SR.GetString(SR.TransactionIndoubt), tx.innerException, tx.DistributedTxId);
}
internal override void CreateBlockingClone(InternalTransaction tx)
{
throw TransactionInDoubtException.Create(SR.GetString(SR.TraceSourceLtm), SR.GetString(SR.TransactionAborted), tx.innerException, tx.DistributedTxId);
}
internal override void CreateAbortingClone(InternalTransaction tx)
{
throw TransactionInDoubtException.Create(SR.GetString(SR.TraceSourceLtm), SR.GetString(SR.TransactionAborted), tx.innerException, tx.DistributedTxId);
}
}
// TransactionStateDelegatedNonMSDTC
//
// This state is the base state for delegated transactions to non-MSDTC promoters.
internal class TransactionStateDelegatedNonMSDTC : TransactionStatePromotedNonMSDTCBase
{
internal override void EnterState(InternalTransaction tx)
{
// Assign the state
CommonEnterState(tx);
// We are never going to have an OletxTransaction for this one.
Oletx.OletxTransaction distributedTx = null;
try
{
// Ask the delegation interface to promote the transaction.
if (DiagnosticTrace.Verbose && tx.durableEnlistment != null)
{
EnlistmentNotificationCallTraceRecord.Trace(SR.GetString(SR.TraceSourceLtm),
tx.durableEnlistment.EnlistmentTraceId,
NotificationCall.Promote
);
}
distributedTx = _TransactionStatePSPEOperation.PSPEPromote(tx);
Debug.Assert((distributedTx == null), string.Format(null, "PSPEPromote for non-MSDTC promotion returned a distributed transaction."));
Debug.Assert((tx.promotedToken != null), string.Format(null, "PSPEPromote for non-MSDTC promotion did not set InternalTransaction.PromotedToken."));
}
catch (TransactionPromotionException e)
{
tx.innerException = e;
if (DiagnosticTrace.Verbose)
{
ExceptionConsumedTraceRecord.Trace(SR.GetString(SR.TraceSourceLtm),
e);
}
}
finally
{
if (tx.promotedToken == null)
{
// There was an exception trying to promote the transaction.
tx.State.ChangeStateAbortedDuringPromotion(tx);
}
}
}
}
// TransactionStateDelegatedSubordinate
//
// This state represents a transaction that is subordinate to another TM and has been
// promoted.
internal class TransactionStateDelegatedSubordinate : TransactionStateDelegatedBase
{
internal override bool PromoteDurable( InternalTransaction tx )
{
return true;
}
internal override void Rollback( InternalTransaction tx, Exception e )
{
// Pass the Rollback through the promotable single phase enlistment to be
// certain it is notified.
if ( tx.innerException == null )
{
tx.innerException = e;
}
tx.PromotedTransaction.Rollback();
_TransactionStatePromotedAborted.EnterState( tx );
}
internal override void ChangeStatePromotedPhase0( InternalTransaction tx )
{
_TransactionStatePromotedPhase0.EnterState( tx );
}
internal override void ChangeStatePromotedPhase1( InternalTransaction tx )
{
_TransactionStatePromotedPhase1.EnterState( tx );
}
}
// TransactionStatePSPEOperation
//
// Someone is trying to enlist for promotable single phase. Don't allow them to do anything
// ----.
internal class TransactionStatePSPEOperation : TransactionState
{
internal override void EnterState(InternalTransaction tx)
{
// No one should ever use this particular version. It has to be overridden because
// the base is abstract.
throw new InvalidOperationException();
}
internal override TransactionStatus get_Status( InternalTransaction tx )
{
throw TransactionException.CreateTransactionStateException( SR.GetString( SR.TraceSourceLtm ), tx.innerException, tx.DistributedTxId);
}
internal void PSPEInitialize(
InternalTransaction tx,
IPromotableSinglePhaseNotification promotableSinglePhaseNotification,
Guid promoterType)
{
Debug.Assert( tx.State == _TransactionStateActive, "PSPEPromote called from state other than TransactionStateActive" );
CommonEnterState( tx );
try
{
// Try to initialize the pspn. If an exception is thrown let it propigate
// all the way up to the caller.
promotableSinglePhaseNotification.Initialize();
// Set the PromoterType for the transaction.
tx.promoterType = promoterType;
}
finally
{
_TransactionStateActive.CommonEnterState( tx );
}
}
// This method will call the intialize method on IPromotableSinglePhaseNotification.
// The tx state will be set to _TransactionStatePhase0 to receive and process further
// enlistments during Phase0.
internal void Phase0PSPEInitialize(
InternalTransaction tx,
IPromotableSinglePhaseNotification promotableSinglePhaseNotification,
Guid promoterType)
{
Debug.Assert( tx.State == _TransactionStatePhase0, "Phase0PSPEInitialize called from state other than _TransactionStatePhase0" );
CommonEnterState( tx );
try
{
// Try to initialize the PSPE. If an exception is thrown let it propagate
// all the way up to the caller.
promotableSinglePhaseNotification.Initialize();
// Set the PromoterType for the transaction.
tx.promoterType = promoterType;
}
finally
{
_TransactionStatePhase0.CommonEnterState(tx);
}
}
internal Oletx.OletxTransaction PSPEPromote( InternalTransaction tx )
{
bool changeToReturnState = true;
TransactionState returnState = tx.State;
Debug.Assert( returnState == _TransactionStateDelegated ||
returnState == _TransactionStateDelegatedSubordinate ||
returnState == _TransactionStateDelegatedNonMSDTC,
"PSPEPromote called from state other than TransactionStateDelegated[NonMSDTC]" );
CommonEnterState( tx );
Oletx.OletxTransaction distributedTx = null;
try
{
if (tx.attemptingPSPEPromote)
{
// There should not already be a PSPEPromote call outstanding.
throw TransactionException.CreateInvalidOperationException(
SR.GetString(SR.TraceSourceLtm),
SR.GetString(SR.PromotedReturnedInvalidValue),
null,
tx.DistributedTxId
);
}
tx.attemptingPSPEPromote = true;
Byte[] propagationToken = tx.promoter.Promote();
// If the PromoterType is NOT MSDTC, then we can't assume that the returned
// byte[] is an MSDTC propagation token and we can't create an OletxTransaction from it.
if (tx.promoterType != TransactionInterop.PromoterTypeDtc)
{
if (propagationToken == null)
{
throw TransactionException.CreateInvalidOperationException(
SR.GetString(SR.TraceSourceLtm),
SR.GetString(SR.PromotedReturnedInvalidValue),
null,
tx.DistributedTxId
);
}
tx.promotedToken = propagationToken;
return null;
}
// From this point forward, we know that the PromoterType is TransactionInterop.PromoterTypeDtc so we can
// treat the propagationToken as an MSDTC propagation token. If one was returned.
if ( propagationToken == null )
{
// If the returned propagationToken is null AND the tx.PromotedTransaction is null, the promote failed.
// But if the PSPE promoter used PSPEPromoteAndConvertToEnlistDurable, tx.PromotedTransaction will NOT be null
// at this point and we just use tx.PromotedTransaction as distributedTx and we don't bother to change to the
// "return state" because the transaction is already in the state it needs to be in.
if (tx.PromotedTransaction == null)
{
// The PSPE has returned an invalid promoted transaction.
throw TransactionException.CreateInvalidOperationException(
SR.GetString(SR.TraceSourceLtm),
SR.GetString(SR.PromotedReturnedInvalidValue),
null,
tx.DistributedTxId
);
}
// The transaction has already transitioned to TransactionStatePromoted, so we don't want
// to change the state to the "returnState" because TransactionStateDelegatedBase.EnterState, would
// try to promote the enlistments again.
changeToReturnState = false;
distributedTx = tx.PromotedTransaction;
}
// At this point, if we haven't yet set distributedTx, we need to get it using the returned
// propagation token. The PSPE promoter must NOT have used PSPEPromoteAndConvertToEnlistDurable.
if (distributedTx == null)
{
try
{
distributedTx = TransactionInterop.GetOletxTransactionFromTransmitterPropigationToken(
propagationToken
);
}
catch (ArgumentException e)
{
// The PSPE has returned an invalid promoted transaction.
throw TransactionException.CreateInvalidOperationException(
SR.GetString(SR.TraceSourceLtm),
SR.GetString(SR.PromotedReturnedInvalidValue),
e,
tx.DistributedTxId
);
}
if (TransactionManager.FindPromotedTransaction(distributedTx.Identifier) != null)
{
// If there is already a promoted transaction then someone has committed an error.
distributedTx.Dispose();
throw TransactionException.CreateInvalidOperationException(
SR.GetString(SR.TraceSourceLtm),
SR.GetString(SR.PromotedTransactionExists),
null,
tx.DistributedTxId
);
}
}
}
finally
{
tx.attemptingPSPEPromote = false;
// If we get here and changeToReturnState is false, the PSPE enlistment must have requested that we
// promote and convert the enlistment to a durable enlistment
// (Transaction.PSPEPromoteAndConvertToEnlistDurable). In that case, the internal transaction is
// already in TransactionStatePromoted, so we don't want to put it BACK into TransactionStateDelegatedBase.
if (changeToReturnState)
{
returnState.CommonEnterState(tx);
}
}
return distributedTx;
}
internal override Enlistment PromoteAndEnlistDurable(
InternalTransaction tx,
Guid resourceManagerIdentifier,
IPromotableSinglePhaseNotification promotableNotification,
ISinglePhaseNotification enlistmentNotification,
EnlistmentOptions enlistmentOptions,
Transaction atomicTransaction
)
{
// This call is only allowed if we have an outstanding call to ITransactionPromoter.Promote.
if (!tx.attemptingPSPEPromote)
{
throw TransactionException.CreateTransactionStateException(SR.GetString(SR.TraceSourceLtm), tx.innerException, tx.DistributedTxId);
}
if (promotableNotification != tx.promoter)
{
throw TransactionException.CreateInvalidOperationException(
SR.GetString(SR.TraceSourceLtm),
SR.GetString(SR.InvalidIPromotableSinglePhaseNotificationSpecified),
null,
tx.DistributedTxId
);
}
Enlistment enlistment;
// First promote the transaction. We do this by simply changing the state of the transaction to Promoted.
// In TransactionStateActive.EnlistPromotableSinglePhase, tx.durableEnlistment was set to point at the InternalEnlistment
// for that PSPE enlistment. We are going to replace that with a "true" durable enlistment here. But we need to
// set tx.durableEnlistment to null BEFORE we promote because if we don't the promotion will attempt to promote
// the tx.durableEnlistment. Because we are doing the EnlistDurable AFTER promotion, it will be a "promoted"
// durable enlistment and we can safely set tx.durableEnlistment to the InternalEnlistment of that Enlistment.
tx.durableEnlistment = null;
tx.promoteState = TransactionState._TransactionStatePromoted;
tx.promoteState.EnterState(tx);
// Now we need to create the durable enlistment that will replace the PSPE enlistment. Use the internalEnlistment of
// this newly created durable enlistment as the tx.durableEnlistment.
enlistment = tx.State.EnlistDurable(tx, resourceManagerIdentifier, enlistmentNotification, enlistmentOptions, atomicTransaction);
tx.durableEnlistment = enlistment.InternalEnlistment;
return enlistment;
}
// TransactionStatePSPEOperation is the only state where this is allowed and we further check to make sure there is
// an outstanding call to ITransactionPromoter.Promote and that the specified promotableNotification matches the
// transaction's promoter object.
internal override void SetDistributedTransactionId(InternalTransaction tx,
IPromotableSinglePhaseNotification promotableNotification,
Guid distributedTransactionIdentifier)
{
// This call is only allowed if we have an outstanding call to ITransactionPromoter.Promote.
if (!tx.attemptingPSPEPromote)
{
throw TransactionException.CreateTransactionStateException(SR.GetString(SR.TraceSourceLtm), tx.innerException, tx.DistributedTxId);
}
if (promotableNotification != tx.promoter)
{
throw TransactionException.CreateInvalidOperationException(
SR.GetString(SR.TraceSourceLtm),
SR.GetString(SR.InvalidIPromotableSinglePhaseNotificationSpecified),
null,
tx.DistributedTxId
);
}
tx.distributedTransactionIdentifierNonMSDTC = distributedTransactionIdentifier;
}
}
// TransactionStateDelegatedP0Wave
//
// This state is exactly the same as TransactionStatePromotedP0Wave with
// the exception that when commit is restarted it is restarted in a different
// way.
internal class TransactionStateDelegatedP0Wave : TransactionStatePromotedP0Wave
{
internal override void Phase0VolatilePrepareDone( InternalTransaction tx )
{
_TransactionStateDelegatedCommitting.EnterState( tx );
}
}
// TransactionStateDelegatedCommitting
//
// The transaction has been promoted but is in the process of committing.
internal class TransactionStateDelegatedCommitting : TransactionStatePromotedCommitting
{
internal override void EnterState( InternalTransaction tx )
{
CommonEnterState( tx );
// Forward this on to the promotable single phase enlisment
System.Threading.Monitor.Exit( tx );
if ( DiagnosticTrace.Verbose )
{
EnlistmentNotificationCallTraceRecord.Trace( SR.GetString( SR.TraceSourceLtm ),
tx.durableEnlistment.EnlistmentTraceId,
NotificationCall.SinglePhaseCommit
);
}
try
{
tx.durableEnlistment.PromotableSinglePhaseNotification.SinglePhaseCommit(
tx.durableEnlistment.SinglePhaseEnlistment );
}
finally
{
#pragma warning disable 0618
//@
System.Threading.Monitor.Enter(tx);
#pragma warning restore 0618
}
}
}
// TransactionStateDelegatedAborting
//
// The transaction has been promoted but is in the process of committing.
internal class TransactionStateDelegatedAborting : TransactionStatePromotedAborted
{
internal override void EnterState( InternalTransaction tx )
{
CommonEnterState( tx );
// The distributed TM is driving the commit processing, so marking of complete
// is done in TransactionStatePromotedPhase0Aborting.EnterState or
// TransactionStatePromotedPhase1Aborting.EnterState.
// Release the lock
System.Threading.Monitor.Exit( tx );
try
{
if ( DiagnosticTrace.Verbose )
{
EnlistmentNotificationCallTraceRecord.Trace( SR.GetString( SR.TraceSourceLtm ),
tx.durableEnlistment.EnlistmentTraceId,
NotificationCall.Rollback
);
}
tx.durableEnlistment.PromotableSinglePhaseNotification.Rollback(
tx.durableEnlistment.SinglePhaseEnlistment );
}
finally
{
#pragma warning disable 0618
//@
System.Threading.Monitor.Enter(tx);
#pragma warning restore 0618
}
}
internal override void BeginCommit( InternalTransaction tx, bool asyncCommit, AsyncCallback asyncCallback, object asyncState )
{
// Initiate the commit process.
throw TransactionException.CreateTransactionStateException( SR.GetString( SR.TraceSourceLtm ), tx.innerException, tx.DistributedTxId);
}
internal override void ChangeStatePromotedAborted( InternalTransaction tx )
{
_TransactionStatePromotedAborted.EnterState( tx );
}
}
}
|