Code:
/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / tx / System / Transactions / Oletx / OletxVolatileEnlistment.cs / 1305376 / OletxVolatileEnlistment.cs
using System; using System.Collections; using System.Diagnostics; using System.Globalization; using System.IO; using System.Reflection; using System.Runtime.InteropServices; using System.Security.Permissions; using System.Threading; using System.Transactions; using System.Transactions.Diagnostics; namespace System.Transactions.Oletx { internal abstract class OletxVolatileEnlistmentContainer { protected RealOletxTransaction realOletxTransaction; protected ArrayList enlistmentList; protected int phase; protected int outstandingNotifications; protected bool collectedVoteYes; protected int incompleteDependentClones; protected bool alreadyVoted; internal abstract void DecrementOutstandingNotifications( bool voteYes ); internal abstract void AddDependentClone(); internal abstract void DependentCloneCompleted(); internal abstract void RollbackFromTransaction(); internal abstract void OutcomeFromTransaction( TransactionStatus outcome ); internal abstract void Committed(); internal abstract void Aborted(); internal abstract void InDoubt(); internal Guid TransactionIdentifier { get { return this.realOletxTransaction.Identifier; } } } internal class OletxPhase0VolatileEnlistmentContainer : OletxVolatileEnlistmentContainer { IPhase0EnlistmentShim phase0EnlistmentShim; bool aborting; bool tmWentDown; internal OletxPhase0VolatileEnlistmentContainer( RealOletxTransaction realOletxTransaction ) { Debug.Assert( null != realOletxTransaction, "Argument is null" ); // This will be set later, after the caller creates the enlistment with the proxy. this.phase0EnlistmentShim = null; this.realOletxTransaction = realOletxTransaction; this.phase = -1; this.aborting = false; this.tmWentDown = false; this.outstandingNotifications = 0; this.incompleteDependentClones = 0; this.alreadyVoted = false; // If anybody votes false, this will get set to false. this.collectedVoteYes = true; this.enlistmentList = new ArrayList(); // This is a new undecided enlistment on the transaction. Do this last since it has side affects. realOletxTransaction.IncrementUndecidedEnlistments(); } internal void TMDown() { if ( DiagnosticTrace.Verbose ) { MethodEnteredTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), "OletxPhase0VolatileEnlistmentContainer.TMDown" ); } this.tmWentDown = true; if ( DiagnosticTrace.Verbose ) { MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), "OletxPhase0VolatileEnlistmentContainer.TMDown" ); } } internal bool NewEnlistmentsAllowed // Be sure to lock this object before calling this. { get { return ( -1 == phase ); } } internal void AddEnlistment( OletxVolatileEnlistment enlistment ) { Debug.Assert( null != enlistment, "Argument is null" ); lock( this ) { if ( -1 != phase ) { throw TransactionException.Create( SR.GetString( SR.TraceSourceOletx ), SR.GetString( SR.TooLate ), null ); } this.enlistmentList.Add( enlistment ); } } internal override void AddDependentClone() { lock( this ) { if ( -1 != phase ) { throw TransactionException.CreateTransactionStateException( SR.GetString( SR.TraceSourceOletx ), null ); } this.incompleteDependentClones++; } } internal override void DependentCloneCompleted() { bool doDecrement = false; lock( this ) { if ( DiagnosticTrace.Verbose ) { string description = "OletxPhase0VolatileEnlistmentContainer.DependentCloneCompleted, outstandingNotifications = " + this.outstandingNotifications.ToString( CultureInfo.CurrentCulture ) + ", incompleteDependentClones = " + this.incompleteDependentClones.ToString( CultureInfo.CurrentCulture ) + ", phase = " + this.phase.ToString( CultureInfo.CurrentCulture ); MethodEnteredTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), description ); } this.incompleteDependentClones--; Debug.Assert( 0 <= this.incompleteDependentClones, "OletxPhase0VolatileEnlistmentContainer.DependentCloneCompleted - incompleteDependentClones < 0" ); // If we have not more incomplete dependent clones and we are in Phase 0, we need to "fake out" a notification completion. if ( ( 0 == this.incompleteDependentClones ) && ( 0 == this.phase ) ) { this.outstandingNotifications++; doDecrement = true; } } if ( doDecrement ) { DecrementOutstandingNotifications( true ); } if ( DiagnosticTrace.Verbose ) { string description = "OletxPhase0VolatileEnlistmentContainer.DependentCloneCompleted"; MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), description ); } } internal override void RollbackFromTransaction() { lock( this ) { if ( DiagnosticTrace.Verbose ) { string description = "OletxPhase0VolatileEnlistmentContainer.RollbackFromTransaction, outstandingNotifications = " + this.outstandingNotifications.ToString( CultureInfo.CurrentCulture ) + ", incompleteDependentClones = " + this.incompleteDependentClones.ToString( CultureInfo.CurrentCulture ); MethodEnteredTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), description ); } if ( ( 0 == phase ) && ( ( 0 < this.outstandingNotifications ) || ( 0 < incompleteDependentClones ) ) ) { this.alreadyVoted = true; // All we are going to do is release the Phase0Enlistment interface because there // is no negative vote to Phase0Request. if ( null != this.Phase0EnlistmentShim ) { this.Phase0EnlistmentShim.Phase0Done( false ); } } } if ( DiagnosticTrace.Verbose ) { string description = "OletxPhase0VolatileEnlistmentContainer.RollbackFromTransaction"; MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), description ); } } internal IPhase0EnlistmentShim Phase0EnlistmentShim { get { IPhase0EnlistmentShim returnValue = null; lock( this ) { returnValue = this.phase0EnlistmentShim; } return returnValue; } set { lock( this ) { // If this.aborting is set to true, then we must have already received a // Phase0Request. This could happen if the transaction aborts after the // enlistment is made, but before we are given the shim. if ( this.aborting || this.tmWentDown ) { value.Phase0Done( false ); } this.phase0EnlistmentShim = value; } } } internal override void DecrementOutstandingNotifications( bool voteYes ) { bool respondToProxy = false; IPhase0EnlistmentShim localPhase0Shim = null; lock( this ) { if ( DiagnosticTrace.Verbose ) { string description = "OletxPhase0VolatileEnlistmentContainer.DecrementOutstandingNotifications, outstandingNotifications = " + this.outstandingNotifications.ToString( CultureInfo.CurrentCulture ) + ", incompleteDependentClones = " + this.incompleteDependentClones.ToString( CultureInfo.CurrentCulture ); MethodEnteredTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), description ); } outstandingNotifications--; Debug.Assert( 0 <= outstandingNotifications, "OletxPhase0VolatileEnlistmentContainer.DecrementOutstandingNotifications - outstandingNotifications < 0" ); this.collectedVoteYes = this.collectedVoteYes && voteYes; if ( ( 0 == this.outstandingNotifications ) && ( 0 == this.incompleteDependentClones ) ) { if ( ( 0 == this.phase ) && ( !this.alreadyVoted ) ) { respondToProxy = true; this.alreadyVoted = true; localPhase0Shim = this.phase0EnlistmentShim; } this.realOletxTransaction.DecrementUndecidedEnlistments(); } } try { if ( respondToProxy ) { if ( null != localPhase0Shim ) { localPhase0Shim.Phase0Done( ( this.collectedVoteYes ) && ( !this.realOletxTransaction.Doomed ) ); } } } catch( COMException ex ) { if ( ( NativeMethods.XACT_E_CONNECTION_DOWN == ex.ErrorCode ) || ( NativeMethods.XACT_E_TMNOTAVAILABLE == ex.ErrorCode ) ) { if ( DiagnosticTrace.Verbose ) { ExceptionConsumedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), ex ); } } // In the case of Phase0, there is a bug in the proxy that causes an XACT_E_PROTOCOL // error if the TM goes down while the enlistment is still active. The Phase0Request is // sent out with abortHint false, but the state of the proxy object is not changed, causing // Phase0Done request to fail with XACT_E_PROTOCOL. // For Prepared, we want to make sure the proxy aborts the transaction. We don't need // to drive the abort to the application here because the Phase1 enlistment will do that. // In other words, treat this as if the proxy said Phase0Request( abortingHint = true ). else if ( NativeMethods.XACT_E_PROTOCOL == ex.ErrorCode ) { this.phase0EnlistmentShim = null; if ( DiagnosticTrace.Verbose ) { ExceptionConsumedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), ex ); } } else { throw; } } if ( DiagnosticTrace.Verbose ) { string description = "OletxPhase0VolatileEnlistmentContainer.DecrementOutstandingNotifications"; MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), description ); } } internal override void OutcomeFromTransaction( TransactionStatus outcome ) { if ( TransactionStatus.Committed == outcome ) { this.Committed(); } else if ( TransactionStatus.Aborted == outcome ) { this.Aborted(); } else if ( TransactionStatus.InDoubt == outcome ) { this.InDoubt(); } else { Debug.Assert( false, "OletxPhase0VolatileEnlistmentContainer.OutcomeFromTransaction, outcome is not Commited or Aborted or InDoubt" ); } } internal override void Committed() { OletxVolatileEnlistment enlistment = null; int localCount = 0; lock( this ) { Debug.Assert( ( 0 == phase ) && ( 0 == this.outstandingNotifications ) ); phase = 2; localCount = this.enlistmentList.Count; } for ( int i = 0; i < localCount; i++ ) { enlistment = this.enlistmentList[i] as OletxVolatileEnlistment; if ( null == enlistment ) { if ( DiagnosticTrace.Critical ) { InternalErrorTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), "" ); } Debug.Assert( false, "OletxPhase1VolatileEnlistmentContainer.Committed, enlistmentList element is not an OletxVolatileEnlistment." ); throw new InvalidOperationException( SR.GetString( SR.InternalError ) ); } enlistment.Commit(); } } internal override void Aborted() { OletxVolatileEnlistment enlistment = null; int localCount = 0; lock( this ) { // Tell all the enlistments that the transaction aborted and let the enlistment // state determine if the notification should be delivered. this.phase = 2; localCount = this.enlistmentList.Count; } for ( int i = 0; i < localCount; i++ ) { enlistment = this.enlistmentList[i] as OletxVolatileEnlistment; if ( null == enlistment ) { if ( DiagnosticTrace.Critical ) { InternalErrorTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), "" ); } Debug.Assert( false, "OletxPhase1VolatileEnlistmentContainer.Aborted, enlistmentList element is not an OletxVolatileEnlistment." ); throw new InvalidOperationException( SR.GetString( SR.InternalError ) ); } enlistment.Rollback(); } } internal override void InDoubt() { OletxVolatileEnlistment enlistment = null; int localCount = 0; lock( this ) { // Tell all the enlistments that the transaction is InDoubt and let the enlistment // state determine if the notification should be delivered. phase = 2; localCount = this.enlistmentList.Count; } for ( int i = 0; i < localCount; i++ ) { enlistment = this.enlistmentList[i] as OletxVolatileEnlistment; if ( null == enlistment ) { if ( DiagnosticTrace.Critical ) { InternalErrorTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), "" ); } Debug.Assert( false, "OletxPhase1VolatileEnlistmentContainer.InDoubt, enlistmentList element is not an OletxVolatileEnlistment." ); throw new InvalidOperationException( SR.GetString( SR.InternalError ) ); } enlistment.InDoubt(); } } internal void Phase0Request( bool abortHint ) { OletxVolatileEnlistment enlistment = null; int localCount = 0; OletxCommittableTransaction committableTx = null; bool commitNotYetCalled = false; lock( this ) { if ( DiagnosticTrace.Verbose ) { string description = "OletxPhase0VolatileEnlistmentContainer.Phase0Request, abortHint = " + abortHint.ToString( CultureInfo.CurrentCulture ) + ", phase = " + this.phase.ToString( CultureInfo.CurrentCulture ); MethodEnteredTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), description ); } this.aborting = abortHint; committableTx = this.realOletxTransaction.committableTransaction; if ( null != committableTx ) { // We are dealing with the committable transaction. If Commit or BeginCommit has NOT been // called, then we are dealing with a situation where the TM went down and we are getting // a bogus Phase0Request with abortHint = false (COMPlus bug 36760/36758). This is an attempt // to not send the app a Prepare request when we know the transaction is going to abort. if (!committableTx.CommitCalled ) { commitNotYetCalled = true; this.aborting = true; } } // It's possible that we are in phase 2 if we got an Aborted outcome from the transaction before we got the // Phase0Request. In both cases, we just respond to the proxy and don't bother telling the enlistments. // They have either already heard about the abort or will soon. if ( ( 2 == this.phase ) || ( -1 == this.phase ) ) { if ( -1 == this.phase ) { this.phase = 0; } // If we got an abort hint or we are the committable transaction and Commit has not yet been called or the TM went down, // we don't want to do any more work on the transaction. The abort notifications will be sent by the phase 1 // enlistment if ( ( this.aborting ) || ( this.tmWentDown ) || ( commitNotYetCalled ) || ( 2 == this.phase ) ) { // There is a possible ---- where we could get the Phase0Request before we are given the // shim. In that case, we will vote "no" when we are given the shim. if ( null != this.phase0EnlistmentShim ) { try { this.phase0EnlistmentShim.Phase0Done( false ); // CSDMain 138031: There is a potential ---- between DTC sending Abort notification and OletxDependentTransaction::Complete is called. // We need to set the alreadyVoted flag to true once we successfully voted, so later we don't vote again when OletxDependentTransaction::Complete is called // Otherwise, in OletxPhase0VolatileEnlistmentContainer::DecrementOutstandingNotifications code path, we are going to call Phase0Done( true ) again // and result in an access violation while accessing the pPhase0EnlistmentAsync member variable of the Phase0Shim object. this.alreadyVoted = true; } // I am not going to check for XACT_E_PROTOCOL here because that check is a workaround for a bug // that only shows up if abortingHint is false. catch ( COMException ex ) { if ( DiagnosticTrace.Verbose ) { ExceptionConsumedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), ex ); } } } return; } outstandingNotifications = enlistmentList.Count; localCount = enlistmentList.Count; // If we don't have any enlistments, then we must have created this container for // delay commit dependent clones only. So we need to fake a notification. if ( 0 == localCount ) { this.outstandingNotifications = 1; } } else // any other phase is bad news. { if ( DiagnosticTrace.Critical ) { InternalErrorTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), "OletxPhase0VolatileEnlistmentContainer.Phase0Request, phase != -1" ); } Debug.Assert( false, "OletxPhase0VolatileEnlistmentContainer.Phase0Request, phase != -1" ); throw new InvalidOperationException( SR.GetString( SR.InternalError ) ); } } // We may not have any Phase0 volatile enlistments, which means that this container // got created solely for delay commit dependent transactions. We need to fake out a // notification completion. if ( 0 == localCount ) { DecrementOutstandingNotifications( true ); } else { for ( int i = 0; i < localCount; i++ ) { enlistment = enlistmentList[i] as OletxVolatileEnlistment; if ( null == enlistment ) { if ( DiagnosticTrace.Critical ) { InternalErrorTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), "" ); } Debug.Assert( false, "OletxPhase0VolatileEnlistmentContainer.Phase0Request, enlistmentList element is not an OletxVolatileEnlistment." ); throw new InvalidOperationException( SR.GetString( SR.InternalError ) ); } // Do the notification outside any locks. Debug.Assert( enlistment.enlistDuringPrepareRequired, "OletxPhase0VolatileEnlistmentContainer.Phase0Request, enlistmentList element not marked as EnlistmentDuringPrepareRequired." ); Debug.Assert( !abortHint, "OletxPhase0VolatileEnlistmentContainer.Phase0Request, abortingHint is true just before sending Prepares." ); enlistment.Prepare( this ); } } if ( DiagnosticTrace.Verbose ) { string description = "OletxPhase0VolatileEnlistmentContainer.Phase0Request, abortHint = " + abortHint.ToString( CultureInfo.CurrentCulture ); MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), description ); } return; } } internal class OletxPhase1VolatileEnlistmentContainer : OletxVolatileEnlistmentContainer { IVoterBallotShim voterBallotShim; internal IntPtr voterHandle = IntPtr.Zero; internal OletxPhase1VolatileEnlistmentContainer( RealOletxTransaction realOletxTransaction ) { Debug.Assert( null != realOletxTransaction, "Argument is null" ); // This will be set later, after the caller creates the enlistment with the proxy. voterBallotShim = null; this.realOletxTransaction = realOletxTransaction; this.phase = -1; this.outstandingNotifications = 0; this.incompleteDependentClones = 0; this.alreadyVoted = false; // If anybody votes false, this will get set to false. this.collectedVoteYes = true; this.enlistmentList = new ArrayList(); // This is a new undecided enlistment on the transaction. Do this last since it has side affects. realOletxTransaction.IncrementUndecidedEnlistments(); } // Returns true if this container is enlisted for Phase 0. internal void AddEnlistment( OletxVolatileEnlistment enlistment ) { Debug.Assert( null != enlistment, "Argument is null" ); lock( this ) { if ( -1 != phase ) { throw TransactionException.Create( SR.GetString( SR.TraceSourceOletx ), SR.GetString( SR.TooLate ), null ); } enlistmentList.Add( enlistment ); } } internal override void AddDependentClone() { lock( this ) { if ( -1 != phase ) { throw TransactionException.CreateTransactionStateException( SR.GetString( SR.TraceSourceOletx ), null ); } // We simply need to block the response to the proxy until all clone is completed. this.incompleteDependentClones++; } } internal override void DependentCloneCompleted() { if ( DiagnosticTrace.Verbose ) { string description = "OletxPhase1VolatileEnlistmentContainer.DependentCloneCompleted, outstandingNotifications = " + this.outstandingNotifications.ToString( CultureInfo.CurrentCulture ) + ", incompleteDependentClones = " + this.incompleteDependentClones.ToString( CultureInfo.CurrentCulture ) + ", phase = " + this.phase.ToString( CultureInfo.CurrentCulture ); MethodEnteredTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), description ); } //Fix for stress bug CSDMain 126887. This is to synchronize with the corresponding AddDependentClone //which takes the container lock while incrementing the incompleteDependentClone count lock(this) { this.incompleteDependentClones--; } Debug.Assert( 0 <= this.outstandingNotifications, "OletxPhase1VolatileEnlistmentContainer.DependentCloneCompleted - DependentCloneCompleted < 0" ); if ( DiagnosticTrace.Verbose ) { string description = "OletxPhase1VolatileEnlistmentContainer.DependentCloneCompleted"; MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), description ); } } internal override void RollbackFromTransaction() { bool voteNo = false; IVoterBallotShim localVoterShim = null; lock( this ) { if ( DiagnosticTrace.Verbose ) { string description = "OletxPhase1VolatileEnlistmentContainer.RollbackFromTransaction, outstandingNotifications = " + this.outstandingNotifications.ToString( CultureInfo.CurrentCulture ) + ", incompleteDependentClones = " + this.incompleteDependentClones.ToString( CultureInfo.CurrentCulture ); MethodEnteredTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), description ); } if ( ( 1 == phase ) && ( 0 < this.outstandingNotifications ) ) { this.alreadyVoted = true; voteNo = true; localVoterShim = this.voterBallotShim; } } if ( voteNo ) { try { if ( null != localVoterShim ) { localVoterShim.Vote( false ); } // We are not going to hear anymore from the proxy if we voted no, so we need to tell the // enlistments to rollback. The state of the OletxVolatileEnlistment will determine whether or // not the notification actually goes out to the app. Aborted(); } catch( COMException ex ) { if ( ( NativeMethods.XACT_E_CONNECTION_DOWN == ex.ErrorCode ) || ( NativeMethods.XACT_E_TMNOTAVAILABLE == ex.ErrorCode ) ) { lock( this ) { // If we are in phase 1, we need to tell the enlistments that the transaction is InDoubt. if ( 1 == phase ) { InDoubt(); } } if ( DiagnosticTrace.Verbose ) { ExceptionConsumedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), ex ); } } else { throw; } } finally { // At this point it is unclear if we will get a notification from DTC or not // it depends on whether or not the transaction was in the process of aborting // already. The only safe thing to do is to ensure that the Handle for the // voter is released at this point. HandleTable.FreeHandle(this.voterHandle); } } if ( DiagnosticTrace.Verbose ) { string description = "OletxPhase1VolatileEnlistmentContainer.RollbackFromTransaction"; MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), description ); } } internal IVoterBallotShim VoterBallotShim { get { IVoterBallotShim returnValue = null; lock( this ) { returnValue = this.voterBallotShim; } return returnValue; } set { lock( this ) { this.voterBallotShim = value; } } } internal override void DecrementOutstandingNotifications( bool voteYes ) { bool respondToProxy = false; IVoterBallotShim localVoterShim = null; lock( this ) { if ( DiagnosticTrace.Verbose ) { string description = "OletxPhase1VolatileEnlistmentContainer.DecrementOutstandingNotifications, outstandingNotifications = " + this.outstandingNotifications.ToString( CultureInfo.CurrentCulture ) + ", incompleteDependentClones = " + this.incompleteDependentClones.ToString( CultureInfo.CurrentCulture ); MethodEnteredTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), description ); } this.outstandingNotifications--; Debug.Assert( 0 <= this.outstandingNotifications, "OletxPhase1VolatileEnlistmentContainer.DecrementOutstandingNotifications - outstandingNotifications < 0" ); collectedVoteYes = collectedVoteYes && voteYes; if ( 0 == outstandingNotifications ) { if ( ( 1 == phase ) && ( ! this.alreadyVoted ) ) { respondToProxy = true; this.alreadyVoted = true; localVoterShim = this.VoterBallotShim; } this.realOletxTransaction.DecrementUndecidedEnlistments(); } } try { if ( respondToProxy ) { if ( ( collectedVoteYes ) && ( ! realOletxTransaction.Doomed ) ) { if ( null != localVoterShim ) { localVoterShim.Vote( true ); } } else // we need to vote no. { try { if ( null != localVoterShim ) { localVoterShim.Vote( false ); } // We are not going to hear anymore from the proxy if we voted no, so we need to tell the // enlistments to rollback. The state of the OletxVolatileEnlistment will determine whether or // not the notification actually goes out to the app. Aborted(); } finally { // At this point it is unclear if we will get a notification from DTC or not // it depends on whether or not the transaction was in the process of aborting // already. The only safe thing to do is to ensure that the Handle for the // voter is released at this point. HandleTable.FreeHandle(this.voterHandle); } } } } catch( COMException ex ) { if ( ( NativeMethods.XACT_E_CONNECTION_DOWN == ex.ErrorCode ) || ( NativeMethods.XACT_E_TMNOTAVAILABLE == ex.ErrorCode ) ) { lock( this ) { // If we are in phase 1, we need to tell the enlistments that the transaction is InDoubt. if ( 1 == phase ) { InDoubt(); } // There is nothing special to do for phase 2. } if ( DiagnosticTrace.Verbose ) { ExceptionConsumedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), ex ); } } else { throw; } } if ( DiagnosticTrace.Verbose ) { string description = "OletxPhase1VolatileEnlistmentContainer.DecrementOutstandingNotifications"; MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), description ); } } internal override void OutcomeFromTransaction( TransactionStatus outcome ) { bool driveAbort = false; bool driveInDoubt = false; lock( this ) { // If we are in Phase 1 and still have outstanding notifications, we need // to drive sending of the outcome to the enlistments. If we are in any // other phase, or we don't have outstanding notifications, we will eventually // get the outcome notification on our OWN voter enlistment, so we will just // wait for that. if ( ( 1 == this.phase ) && ( 0 < this.outstandingNotifications ) ) { if ( TransactionStatus.Aborted == outcome ) { driveAbort = true; } else if ( TransactionStatus.InDoubt == outcome ) { driveInDoubt = true; } else { Debug.Assert( false, "OletxPhase1VolatileEnlistmentContainer.OutcomeFromTransaction, outcome is not Aborted or InDoubt" ); } } } if ( driveAbort ) { Aborted(); } if ( driveInDoubt ) { InDoubt(); } } internal override void Committed() { OletxVolatileEnlistment enlistment = null; int localPhase1Count = 0; lock( this ) { phase = 2; localPhase1Count = this.enlistmentList.Count; } for ( int i = 0; i < localPhase1Count; i++ ) { enlistment = this.enlistmentList[i] as OletxVolatileEnlistment; if ( null == enlistment ) { if ( DiagnosticTrace.Critical ) { InternalErrorTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), "" ); } Debug.Assert( false, "OletxPhase1VolatileEnlistmentContainer.Committed, enlistmentList element is not an OletxVolatileEnlistment." ); throw new InvalidOperationException( SR.GetString( SR.InternalError ) ); } enlistment.Commit(); } } internal override void Aborted() { OletxVolatileEnlistment enlistment = null; int localPhase1Count = 0; lock( this ) { phase = 2; localPhase1Count = this.enlistmentList.Count; } for ( int i = 0; i < localPhase1Count; i++ ) { enlistment = this.enlistmentList[i] as OletxVolatileEnlistment; if ( null == enlistment ) { if ( DiagnosticTrace.Critical ) { InternalErrorTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), "" ); } Debug.Assert( false, "OletxPhase1VolatileEnlistmentContainer.Aborted, enlistmentList element is not an OletxVolatileEnlistment." ); throw new InvalidOperationException( SR.GetString( SR.InternalError ) ); } enlistment.Rollback(); } } internal override void InDoubt() { OletxVolatileEnlistment enlistment = null; int localPhase1Count = 0; lock( this ) { phase = 2; localPhase1Count = this.enlistmentList.Count; } for ( int i = 0; i < localPhase1Count; i++ ) { enlistment = this.enlistmentList[i] as OletxVolatileEnlistment; if ( null == enlistment ) { if ( DiagnosticTrace.Critical ) { InternalErrorTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), "" ); } Debug.Assert( false, "OletxPhase1VolatileEnlistmentContainer.InDoubt, enlistmentList element is not an OletxVolatileEnlistment." ); throw new InvalidOperationException( SR.GetString( SR.InternalError ) ); } enlistment.InDoubt(); } } internal void VoteRequest() { OletxVolatileEnlistment enlistment = null; int localPhase1Count = 0; bool voteNo = false; lock( this ) { if ( DiagnosticTrace.Verbose ) { string description = "OletxPhase1VolatileEnlistmentContainer.VoteRequest"; MethodEnteredTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), description ); } phase = 1; // If we still have incomplete dependent clones, vote no now. if ( 0 < this.incompleteDependentClones ) { voteNo = true; this.outstandingNotifications = 1; } else { this.outstandingNotifications = this.enlistmentList.Count; localPhase1Count = this.enlistmentList.Count; // We may not have an volatile phase 1 enlistments, which means that this // container was created only for non-delay commit dependent clones. If that // is the case, fake out a notification and response. if ( 0 == localPhase1Count ) { this.outstandingNotifications = 1; } } this.realOletxTransaction.TooLateForEnlistments = true; } if ( voteNo ) { DecrementOutstandingNotifications( false ); } else if ( 0 == localPhase1Count ) { DecrementOutstandingNotifications( true ); } else { for ( int i = 0; i < localPhase1Count; i++ ) { enlistment = this.enlistmentList[i] as OletxVolatileEnlistment; if ( null == enlistment ) { if ( DiagnosticTrace.Critical ) { InternalErrorTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), "" ); } Debug.Assert( false, "OletxPhase1VolatileEnlistmentContainer.VoteRequest, enlistmentList element is not an OletxVolatileEnlistment." ); throw new InvalidOperationException( SR.GetString( SR.InternalError ) ); } enlistment.Prepare( this ); } } if ( DiagnosticTrace.Verbose ) { string description = "OletxPhase1VolatileEnlistmentContainer.VoteRequest"; MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), description ); } } } class OletxVolatileEnlistment : OletxBaseEnlistment, IPromotedEnlistment { enum OletxVolatileEnlistmentState { Active, Preparing, Committing, Aborting, Prepared, Aborted, InDoubt, Done } IEnlistmentNotificationInternal iEnlistmentNotification; OletxVolatileEnlistmentState state = OletxVolatileEnlistmentState.Active; OletxVolatileEnlistmentContainer container; internal bool enlistDuringPrepareRequired; // This is used if the transaction outcome is received while a prepare request // is still outstanding to an app. Active means no outcome, yet. Aborted means // we should tell the app Aborted. InDoubt means tell the app InDoubt. This // should never be Committed because we shouldn't receive a Committed notification // from the proxy while we have a Prepare outstanding. TransactionStatus pendingOutcome; internal OletxVolatileEnlistment( IEnlistmentNotificationInternal enlistmentNotification, EnlistmentOptions enlistmentOptions, OletxTransaction oletxTransaction ) : base( null, oletxTransaction ) { this.iEnlistmentNotification = enlistmentNotification; this.enlistDuringPrepareRequired = (enlistmentOptions & EnlistmentOptions.EnlistDuringPrepareRequired) != 0; // We get a container when we are asked to vote. this.container = null; pendingOutcome = TransactionStatus.Active; if ( DiagnosticTrace.Information ) { EnlistmentTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), this.InternalTraceIdentifier, EnlistmentType.Volatile, enlistmentOptions ); } } internal void Prepare( OletxVolatileEnlistmentContainer container ) { OletxVolatileEnlistmentState localState = OletxVolatileEnlistmentState.Active; IEnlistmentNotificationInternal localEnlistmentNotification = null; lock( this ) { localEnlistmentNotification = iEnlistmentNotification; // The app may have already called EnlistmentDone. If this occurs, don't bother sending // the notification to the app. if ( OletxVolatileEnlistmentState.Active == state ) { localState = state = OletxVolatileEnlistmentState.Preparing; } else { localState = state; } this.container = container; } // Tell the application to do the work. if ( OletxVolatileEnlistmentState.Preparing == localState ) { if ( null != localEnlistmentNotification ) { if ( DiagnosticTrace.Verbose ) { EnlistmentNotificationCallTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), this.InternalTraceIdentifier, NotificationCall.Prepare ); } localEnlistmentNotification.Prepare( this ); } else { if ( DiagnosticTrace.Critical ) { InternalErrorTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), "" ); } Debug.Assert( false, "OletxVolatileEnlistment.Prepare, no enlistmentNotification member." ); throw new InvalidOperationException( SR.GetString( SR.InternalError ) ); } } else if ( OletxVolatileEnlistmentState.Done == localState ) { // Voting yes because it was an early read-only vote. container.DecrementOutstandingNotifications( true ); // We must have had a ---- between EnlistmentDone and the proxy telling // us Phase0Request. Just return. return; } // It is okay to be in Prepared state if we are edpr=true because we already // did our prepare in Phase0. else if ( ( OletxVolatileEnlistmentState.Prepared == localState ) && ( this.enlistDuringPrepareRequired ) ) { container.DecrementOutstandingNotifications( true ); return; } else if ( ( OletxVolatileEnlistmentState.Aborting == localState ) || ( OletxVolatileEnlistmentState.Aborted == localState ) ) { // An abort has ----d with this volatile Prepare // decrement the outstanding notifications making sure to vote no. container.DecrementOutstandingNotifications( false ); return; } else { if ( DiagnosticTrace.Critical ) { InternalErrorTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), "" ); } Debug.Assert( false, "OletxVolatileEnlistment.Prepare, invalid state." ); throw new InvalidOperationException( SR.GetString( SR.InternalError ) ); } } internal void Commit() { OletxVolatileEnlistmentState localState = OletxVolatileEnlistmentState.Active; IEnlistmentNotificationInternal localEnlistmentNotification = null; lock( this ) { // The app may have already called EnlistmentDone. If this occurs, don't bother sending // the notification to the app and we don't need to tell the proxy. if ( OletxVolatileEnlistmentState.Prepared == state ) { localState = state = OletxVolatileEnlistmentState.Committing; localEnlistmentNotification = iEnlistmentNotification; } else { localState = state; } } // Tell the application to do the work. if ( OletxVolatileEnlistmentState.Committing == localState ) { if ( null != localEnlistmentNotification ) { if ( DiagnosticTrace.Verbose ) { EnlistmentNotificationCallTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), this.InternalTraceIdentifier, NotificationCall.Commit ); } localEnlistmentNotification.Commit( this ); } else { if ( DiagnosticTrace.Critical ) { InternalErrorTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), "" ); } Debug.Assert( false, "OletxVolatileEnlistment.Commit, no enlistmentNotification member." ); throw new InvalidOperationException( SR.GetString( SR.InternalError ) ); } } else if ( OletxVolatileEnlistmentState.Done == localState ) { // Early Exit - state was Done } else { if ( DiagnosticTrace.Critical ) { InternalErrorTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), "" ); } Debug.Assert( false, "OletxVolatileEnlistment.Commit, invalid state." ); throw new InvalidOperationException( SR.GetString( SR.InternalError ) ); } } internal void Rollback() { OletxVolatileEnlistmentState localState = OletxVolatileEnlistmentState.Active; IEnlistmentNotificationInternal localEnlistmentNotification = null; lock( this ) { // The app may have already called EnlistmentDone. If this occurs, don't bother sending // the notification to the app and we don't need to tell the proxy. if ( ( OletxVolatileEnlistmentState.Prepared == state ) || ( OletxVolatileEnlistmentState.Active == state ) ) { localState = state = OletxVolatileEnlistmentState.Aborting; localEnlistmentNotification = iEnlistmentNotification; } else { if ( OletxVolatileEnlistmentState.Preparing == state ) { pendingOutcome = TransactionStatus.Aborted; } localState = state; } } // Tell the application to do the work. if ( OletxVolatileEnlistmentState.Aborting == localState ) { if ( null != localEnlistmentNotification ) { if ( DiagnosticTrace.Verbose ) { EnlistmentNotificationCallTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), this.InternalTraceIdentifier, NotificationCall.Rollback ); } localEnlistmentNotification.Rollback( this ); } // There is a small ---- where Rollback could be called when the enlistment is already // aborting the transaciton, so just ignore that call. When the app enlistment // finishes responding to its Rollback notification with EnlistmentDone, things will get // cleaned up. } else if ( OletxVolatileEnlistmentState.Preparing == localState ) { // We need to tolerate this state, but we have already marked the // enlistment as pendingRollback, so there is nothing else to do here. } else if ( OletxVolatileEnlistmentState.Done == localState ) { // Early Exit - state was Done } else { if ( DiagnosticTrace.Critical ) { InternalErrorTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), "" ); } Debug.Assert( false, "OletxVolatileEnlistment.Rollback, invalid state." ); throw new InvalidOperationException( SR.GetString( SR.InternalError ) ); } } internal void InDoubt() { OletxVolatileEnlistmentState localState = OletxVolatileEnlistmentState.Active; IEnlistmentNotificationInternal localEnlistmentNotification = null; lock( this ) { // The app may have already called EnlistmentDone. If this occurs, don't bother sending // the notification to the app and we don't need to tell the proxy. if ( OletxVolatileEnlistmentState.Prepared == state ) { localState = state = OletxVolatileEnlistmentState.InDoubt; localEnlistmentNotification = iEnlistmentNotification; } else { if ( OletxVolatileEnlistmentState.Preparing == state ) { pendingOutcome = TransactionStatus.InDoubt; } localState = state; } } // Tell the application to do the work. if ( OletxVolatileEnlistmentState.InDoubt == localState ) { if ( null != localEnlistmentNotification ) { if ( DiagnosticTrace.Verbose ) { EnlistmentNotificationCallTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), this.InternalTraceIdentifier, NotificationCall.InDoubt ); } localEnlistmentNotification.InDoubt( this ); } else { if ( DiagnosticTrace.Critical ) { InternalErrorTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), "" ); } Debug.Assert( false, "OletxVolatileEnlistment.InDoubt, no enlistmentNotification member." ); throw new InvalidOperationException( SR.GetString( SR.InternalError ) ); } } else if ( OletxVolatileEnlistmentState.Preparing == localState ) { // We have already set pendingOutcome, so there is nothing else to do. } else if ( OletxVolatileEnlistmentState.Done == localState ) { // Early Exit - state was Done } else { if ( DiagnosticTrace.Critical ) { InternalErrorTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), "" ); } Debug.Assert( false, "OletxVolatileEnlistment.InDoubt, invalid state." ); throw new InvalidOperationException( SR.GetString( SR.InternalError ) ); } } void IPromotedEnlistment.EnlistmentDone() { if ( DiagnosticTrace.Verbose ) { MethodEnteredTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), "OletxEnlistment.EnlistmentDone" ); EnlistmentCallbackPositiveTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), this.InternalTraceIdentifier, EnlistmentCallback.Done ); } OletxVolatileEnlistmentState localState = OletxVolatileEnlistmentState.Active; OletxVolatileEnlistmentContainer localContainer = null; lock( this ) { localState = state; localContainer = container; if ( ( OletxVolatileEnlistmentState.Active != state ) && ( OletxVolatileEnlistmentState.Preparing != state ) && ( OletxVolatileEnlistmentState.Aborting != state ) && ( OletxVolatileEnlistmentState.Committing != state ) && ( OletxVolatileEnlistmentState.InDoubt != state ) ) { throw TransactionException.CreateEnlistmentStateException( SR.GetString( SR.TraceSourceOletx ), null ); } state = OletxVolatileEnlistmentState.Done; } // For the Preparing state, we need to decrement the outstanding // count with the container. If the state is Active, it is an early vote so we // just stay in the Done state and when we get the Prepare, we will vote appropriately. if ( OletxVolatileEnlistmentState.Preparing == localState ) { if ( null != localContainer ) { // Specify true. If aborting, it is okay because the transaction is already // aborting. localContainer.DecrementOutstandingNotifications( true ); } } if ( DiagnosticTrace.Verbose ) { MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), "OletxEnlistment.EnlistmentDone" ); } } void IPromotedEnlistment.Prepared() { if ( DiagnosticTrace.Verbose ) { MethodEnteredTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), "OletxPreparingEnlistment.Prepared" ); EnlistmentCallbackPositiveTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), this.InternalTraceIdentifier, EnlistmentCallback.Prepared ); } OletxVolatileEnlistmentContainer localContainer = null; TransactionStatus localPendingOutcome = TransactionStatus.Active; lock( this ) { if ( OletxVolatileEnlistmentState.Preparing != state ) { throw TransactionException.CreateEnlistmentStateException( SR.GetString( SR.TraceSourceOletx ), null ); } state = OletxVolatileEnlistmentState.Prepared; localPendingOutcome = pendingOutcome; if ( null == container ) { if ( DiagnosticTrace.Critical ) { InternalErrorTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), "" ); } Debug.Assert( false, "OletxVolatileEnlistment.Prepared, no container member." ); throw new InvalidOperationException( SR.GetString( SR.InternalError ) ); } localContainer = container; } // Vote yes. localContainer.DecrementOutstandingNotifications( true ); switch ( localPendingOutcome ) { case TransactionStatus.Active : { // nothing to do. Everything is proceeding as normal. break; } case TransactionStatus.Aborted : { // The transaction aborted while the Prepare was outstanding. // We need to tell the app to rollback. Rollback(); break; } case TransactionStatus.InDoubt : { // The transaction went InDoubt while the Prepare was outstanding. // We need to tell the app. InDoubt(); break; } default : { // This shouldn't happen. if ( DiagnosticTrace.Critical ) { InternalErrorTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), "" ); } Debug.Assert( false, "OletxVolatileEnlistment.Prepared, invalid pending outcome value." ); throw new InvalidOperationException( SR.GetString( SR.InternalError ) ); } } if ( DiagnosticTrace.Verbose ) { MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), "OletxPreparingEnlistment.Prepared" ); } } void IPromotedEnlistment.ForceRollback() { ((IPromotedEnlistment)this).ForceRollback( null ); } void IPromotedEnlistment.ForceRollback(Exception e) { if ( DiagnosticTrace.Verbose ) { MethodEnteredTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), "OletxPreparingEnlistment.ForceRollback" ); } if ( DiagnosticTrace.Warning ) { EnlistmentCallbackNegativeTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), this.InternalTraceIdentifier, EnlistmentCallback.ForceRollback ); } OletxVolatileEnlistmentContainer localContainer = null; lock( this ) { if ( OletxVolatileEnlistmentState.Preparing != state ) { throw TransactionException.CreateEnlistmentStateException( SR.GetString( SR.TraceSourceOletx ), null ); } // There are no more notifications that need to happen on this enlistment. state = OletxVolatileEnlistmentState.Done; if ( null == container ) { if ( DiagnosticTrace.Critical ) { InternalErrorTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), "" ); } Debug.Assert( false, "OletxVolatileEnlistment.ForceRollback, no container member." ); throw new InvalidOperationException( SR.GetString( SR.InternalError ) ); } localContainer = container; } Interlocked.CompareExchange( ref this.oletxTransaction.realOletxTransaction.innerException, e, null ); // Vote no. localContainer.DecrementOutstandingNotifications( false ); if ( DiagnosticTrace.Verbose ) { MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), "OletxPreparingEnlistment.ForceRollback" ); } } void IPromotedEnlistment.Committed() { throw new InvalidOperationException(); } void IPromotedEnlistment.Aborted() { throw new InvalidOperationException(); } void IPromotedEnlistment.Aborted(Exception e) { throw new InvalidOperationException(); } void IPromotedEnlistment.InDoubt() { throw new InvalidOperationException(); } void IPromotedEnlistment.InDoubt(Exception e) { throw new InvalidOperationException(); } byte[] IPromotedEnlistment.GetRecoveryInformation() { throw TransactionException.CreateInvalidOperationException( SR.GetString( SR.TraceSourceOletx ), SR.GetString( SR.VolEnlistNoRecoveryInfo), null ); } InternalEnlistment IPromotedEnlistment.InternalEnlistment { get { return this.internalEnlistment; } set { this.internalEnlistment = value; } } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- SystemIPAddressInformation.cs
- DesignerDataSourceView.cs
- AnchoredBlock.cs
- Mouse.cs
- XmlAttributeCache.cs
- FrameworkTemplate.cs
- TabControl.cs
- ExpressionPrefixAttribute.cs
- DataGridViewColumn.cs
- PropertyGridEditorPart.cs
- DataGridViewCellCancelEventArgs.cs
- Int32EqualityComparer.cs
- TextPatternIdentifiers.cs
- SecurityDescriptor.cs
- XmlElementAttributes.cs
- _SslSessionsCache.cs
- PartitionResolver.cs
- Size3D.cs
- ToolStripTextBox.cs
- NativeMethodsOther.cs
- XmlDataSourceNodeDescriptor.cs
- SecurityChannelFactory.cs
- MDIClient.cs
- ValidationResult.cs
- ProfilePropertySettings.cs
- RootBuilder.cs
- HttpWriter.cs
- _UncName.cs
- SqlInternalConnection.cs
- shaper.cs
- ZoomPercentageConverter.cs
- SettingsPropertyCollection.cs
- Directory.cs
- DrawTreeNodeEventArgs.cs
- _HeaderInfo.cs
- RadioButtonRenderer.cs
- ClientFormsAuthenticationCredentials.cs
- ImageBrush.cs
- RepeaterItem.cs
- Validator.cs
- LocalizationComments.cs
- FileRegion.cs
- HeaderUtility.cs
- EntitySetBaseCollection.cs
- DataContext.cs
- ReflectionPermission.cs
- Monitor.cs
- PropertyEmitter.cs
- ViewGenResults.cs
- AutoCompleteStringCollection.cs
- PrivacyNoticeElement.cs
- ColumnCollection.cs
- RtfNavigator.cs
- AuthorizationRule.cs
- LayoutSettings.cs
- ColumnMapCopier.cs
- Root.cs
- InstalledVoice.cs
- ColumnResult.cs
- RegistrationServices.cs
- AsyncOperationContext.cs
- DropShadowBitmapEffect.cs
- CompilerParameters.cs
- FixedSOMGroup.cs
- PropertyHelper.cs
- Claim.cs
- MergablePropertyAttribute.cs
- MenuItem.cs
- SerializationHelper.cs
- SpStreamWrapper.cs
- Vector3DIndependentAnimationStorage.cs
- UnmanagedMemoryStreamWrapper.cs
- Section.cs
- DataGridViewCellStateChangedEventArgs.cs
- AppDomainManager.cs
- TableLayoutPanelCellPosition.cs
- DataGridLengthConverter.cs
- StrokeNodeOperations2.cs
- LookupTables.cs
- Underline.cs
- TransactionScopeDesigner.cs
- TextComposition.cs
- ControlAdapter.cs
- CompareValidator.cs
- BufferAllocator.cs
- ExpressionDumper.cs
- WebPartZone.cs
- WindowsSolidBrush.cs
- SqlDataSourceCache.cs
- SchemaAttDef.cs
- IISMapPath.cs
- RijndaelManaged.cs
- PrivilegeNotHeldException.cs
- VisualProxy.cs
- TextBoxBase.cs
- RepeatButtonAutomationPeer.cs
- MenuItemBindingCollection.cs
- DataGridViewHitTestInfo.cs
- SafeReadContext.cs
- ComplexTypeEmitter.cs