OletxVolatileEnlistment.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / 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.
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

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK