File: System\Transactions\Enlistment.cs
Project: ndp\cdf\src\NetFx20\System.Transactions\System.Transactions.csproj (System.Transactions)
//-----------------------------------------------------------------------------
// <copyright file="Enlistment.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//-----------------------------------------------------------------------------
 
namespace System.Transactions
{
    using System;
    using System.Diagnostics;
    using System.Globalization;
    using System.Threading;
    using System.Transactions;
    using System.Transactions.Diagnostics;
 
    internal interface IPromotedEnlistment
    {
        void EnlistmentDone();
 
        void Prepared();
 
        void ForceRollback();
 
        void ForceRollback(Exception e);
 
        void Committed();
 
        void Aborted();
 
        void Aborted(Exception e);
 
        void InDoubt();
 
        void InDoubt(Exception e);
 
        byte[] GetRecoveryInformation();
 
        InternalEnlistment InternalEnlistment
        {
            get;
            set;
        }
    }
 
 
    //
    // InternalEnlistment by itself can support a Phase0 volatile enlistment.
    // There are derived classes to support durable, phase1 volatile & PSPE
    // enlistments.
    //
    class InternalEnlistment : ISinglePhaseNotificationInternal
    {
        // Storage for the state of the enlistment.
        internal EnlistmentState twoPhaseState;
 
        // Interface implemented by the enlistment owner for notifications
        protected IEnlistmentNotification twoPhaseNotifications;
 
        // Store a reference to the single phase notification interface in case
        // the enlisment supports it.
        protected ISinglePhaseNotification singlePhaseNotifications;
 
        // Reference to the containing transaction.
        protected InternalTransaction transaction;
 
        // Reference to the lightweight transaction.
        Transaction atomicTransaction;
 
        // The EnlistmentTraceIdentifier for this enlistment.
        private EnlistmentTraceIdentifier traceIdentifier;
 
        // Unique value amongst all enlistments for a given internal transaction.
        int enlistmentId;
 
        internal Guid DistributedTxId
        {
            get
            {
                Guid returnValue = Guid.Empty;
 
                if (this.Transaction != null )
                {
                    returnValue = this.Transaction.DistributedTxId;
                }
                return returnValue;
            }
        }
 
        // Parent Enlistment Object
        Enlistment enlistment;
        PreparingEnlistment preparingEnlistment;
        SinglePhaseEnlistment singlePhaseEnlistment;
 
        // If this enlistment is promoted store the object it delegates to.
        IPromotedEnlistment promotedEnlistment;
 
        // For Recovering Enlistments
        protected InternalEnlistment(
            Enlistment enlistment,
            IEnlistmentNotification twoPhaseNotifications
            )
        {
            Debug.Assert(this is RecoveringInternalEnlistment, "this is RecoveringInternalEnlistment");
            this.enlistment = enlistment;
            this.twoPhaseNotifications = twoPhaseNotifications;
            this.enlistmentId = 1;
            this.traceIdentifier = EnlistmentTraceIdentifier.Empty;
        }
 
 
        // For Promotable Enlistments
        protected InternalEnlistment(
            Enlistment enlistment,
            InternalTransaction transaction, 
            Transaction atomicTransaction
            )
        {
            Debug.Assert(this is PromotableInternalEnlistment, "this is PromotableInternalEnlistment");
            this.enlistment = enlistment;
            this.transaction = transaction;
            this.atomicTransaction = atomicTransaction;
            this.enlistmentId = transaction.enlistmentCount++;
            this.traceIdentifier = EnlistmentTraceIdentifier.Empty;
        }
 
 
        internal InternalEnlistment(
            Enlistment enlistment,
            InternalTransaction transaction, 
            IEnlistmentNotification twoPhaseNotifications,
            ISinglePhaseNotification singlePhaseNotifications,
            Transaction atomicTransaction
            )
        {
            this.enlistment = enlistment;
            this.transaction = transaction;
            this.twoPhaseNotifications = twoPhaseNotifications;
            this.singlePhaseNotifications = singlePhaseNotifications;
            this.atomicTransaction = atomicTransaction;
            this.enlistmentId = transaction.enlistmentCount++;
            this.traceIdentifier = EnlistmentTraceIdentifier.Empty;
        }
 
 
        internal InternalEnlistment(
            Enlistment enlistment,
            IEnlistmentNotification twoPhaseNotifications,
            InternalTransaction transaction,
            Transaction atomicTransaction
            )
        {
            this.enlistment = enlistment;
            this.twoPhaseNotifications = twoPhaseNotifications;
            this.transaction = transaction;
            this.atomicTransaction = atomicTransaction;
        }
 
 
        internal EnlistmentState State
        {
            get
            {
                return this.twoPhaseState;
            }
            
            set
            {
                this.twoPhaseState = value;
            }
        }
 
 
        internal Enlistment Enlistment
        {
            get
            {
                return this.enlistment;
            }
        }
 
 
        internal PreparingEnlistment PreparingEnlistment
        {
            get
            {
                if (this.preparingEnlistment == null)
                {
                    // If there is a ---- here one of the objects would simply be garbage collected.
                    this.preparingEnlistment = new PreparingEnlistment(this);
                }
                return this.preparingEnlistment;
            }
        }
 
 
        internal SinglePhaseEnlistment SinglePhaseEnlistment
        {
            get
            {
                if (this.singlePhaseEnlistment == null)
                {
                    // If there is a ---- here one of the objects would simply be garbage collected.
                    this.singlePhaseEnlistment = new SinglePhaseEnlistment(this);
                }
                return this.singlePhaseEnlistment;
            }
        }
 
 
        internal InternalTransaction Transaction
        {
            get
            {
                return this.transaction;
            }
        }
 
 
        internal virtual object SyncRoot
        {
            get
            {
                Debug.Assert(this.transaction != null, "this.transaction != null");
                return this.transaction;
            }
        }
 
 
        internal IEnlistmentNotification EnlistmentNotification
        {
            get
            {
                return this.twoPhaseNotifications;
            }
        }
 
 
        internal ISinglePhaseNotification SinglePhaseNotification
        {
            get
            {
                return this.singlePhaseNotifications;
            }
        }
 
 
        internal virtual IPromotableSinglePhaseNotification PromotableSinglePhaseNotification
        {
            get
            {
                Debug.Assert(false, "PromotableSinglePhaseNotification called for a non promotable enlistment.");
                throw new NotImplementedException();
            }
        }
 
 
        internal IPromotedEnlistment PromotedEnlistment
        {
            get
            {
                return this.promotedEnlistment;
            }
 
            set
            {
                this.promotedEnlistment = value;
            }
        }
 
 
        internal EnlistmentTraceIdentifier EnlistmentTraceId
        {
            get
            {
                if (this.traceIdentifier == EnlistmentTraceIdentifier.Empty)
                {
                    lock (this.SyncRoot)
                    {
                        if (this.traceIdentifier == EnlistmentTraceIdentifier.Empty)
                        {
                            EnlistmentTraceIdentifier temp;
                            if (null != this.atomicTransaction)
                            {
                                temp = new EnlistmentTraceIdentifier(
                                    Guid.Empty,
                                    this.atomicTransaction.TransactionTraceId,
                                    this.enlistmentId
                                    );
                            }
                            else
                            {
                                temp = new EnlistmentTraceIdentifier(
                                    Guid.Empty,
                                    new TransactionTraceIdentifier(
                                        InternalTransaction.InstanceIdentifier +
                                            Convert.ToString(Interlocked.Increment(ref InternalTransaction.nextHash), CultureInfo.InvariantCulture),
                                        0),
                                    this.enlistmentId
                                    );
                            }
                            Thread.MemoryBarrier();
                            this.traceIdentifier = temp;
                        }
                    }
                }
                return this.traceIdentifier;
            }
        }
 
 
        internal virtual void FinishEnlistment()
        {
            // Note another enlistment finished.
            this.Transaction.phase0Volatiles.preparedVolatileEnlistments++;
            CheckComplete();
        }
 
 
        internal virtual void CheckComplete()
        {
            // Make certain we increment the right list.
            Debug.Assert(this.Transaction.phase0Volatiles.preparedVolatileEnlistments <=
                this.Transaction.phase0Volatiles.volatileEnlistmentCount + this.Transaction.phase0Volatiles.dependentClones);
 
            // Check to see if all of the volatile enlistments are done.
            if (this.Transaction.phase0Volatiles.preparedVolatileEnlistments == 
                this.Transaction.phase0VolatileWaveCount + this.Transaction.phase0Volatiles.dependentClones)
            {
                this.Transaction.State.Phase0VolatilePrepareDone(this.Transaction);
            }
        }
 
 
        internal virtual Guid ResourceManagerIdentifier
        {
            get
            {
                Debug.Assert(false, "ResourceManagerIdentifier called for non durable enlistment");
                throw new NotImplementedException();
            }
        }
 
 
        void ISinglePhaseNotificationInternal.SinglePhaseCommit(
            IPromotedEnlistment singlePhaseEnlistment
            )
        {
            bool spcCommitted = false; 
            this.promotedEnlistment = singlePhaseEnlistment;
            try
            {
                this.singlePhaseNotifications.SinglePhaseCommit(this.SinglePhaseEnlistment);
                spcCommitted = true; 
            }
            finally
            {
                if (!spcCommitted)
                {
                    this.SinglePhaseEnlistment.InDoubt();
                }
            }
        }
 
 
        void IEnlistmentNotificationInternal.Prepare(
            IPromotedEnlistment preparingEnlistment
            )
        {
            this.promotedEnlistment = preparingEnlistment;
            this.twoPhaseNotifications.Prepare(this.PreparingEnlistment);
        }
 
 
        void IEnlistmentNotificationInternal.Commit(
            IPromotedEnlistment enlistment
            )
        {
            this.promotedEnlistment = enlistment;
            this.twoPhaseNotifications.Commit(this.Enlistment);
        }
        
 
        void IEnlistmentNotificationInternal.Rollback(
            IPromotedEnlistment enlistment
            )
        {
            this.promotedEnlistment = enlistment;
            this.twoPhaseNotifications.Rollback(this.Enlistment);
        }
 
 
        void IEnlistmentNotificationInternal.InDoubt(
            IPromotedEnlistment enlistment
            )
        {
            this.promotedEnlistment = enlistment;
            this.twoPhaseNotifications.InDoubt(this.Enlistment);
        }
 
    }
 
 
    class DurableInternalEnlistment : InternalEnlistment
    {
        // Resource Manager Identifier for this enlistment if it is durable
        internal Guid resourceManagerIdentifier;
 
        internal DurableInternalEnlistment(
            Enlistment enlistment,
            Guid resourceManagerIdentifier,
            InternalTransaction transaction, 
            IEnlistmentNotification twoPhaseNotifications,
            ISinglePhaseNotification singlePhaseNotifications,
            Transaction atomicTransaction
            ) :
            base(enlistment, transaction, twoPhaseNotifications, singlePhaseNotifications, atomicTransaction)
        {
            this.resourceManagerIdentifier = resourceManagerIdentifier;
        }
 
 
        protected DurableInternalEnlistment(
            Enlistment enlistment,
            IEnlistmentNotification twoPhaseNotifications
            ) : base(enlistment, twoPhaseNotifications)
        {
        }
 
 
        internal override Guid ResourceManagerIdentifier
        {
            get
            {
                return resourceManagerIdentifier;
            }
        }
    }
 
 
    //
    // Since RecoveringInternalEnlistment does not have a transaction it must take
    // a separate object as its sync root.
    //
    class RecoveringInternalEnlistment : DurableInternalEnlistment
    {
        object syncRoot;
 
        internal RecoveringInternalEnlistment(
            Enlistment enlistment,
            IEnlistmentNotification twoPhaseNotifications,
            object syncRoot
            ) : base(enlistment, twoPhaseNotifications)
        {
            this.syncRoot = syncRoot;
        }
        
        internal override object SyncRoot
        {
            get
            {
                return this.syncRoot;
            }
        }
    }
 
 
    class PromotableInternalEnlistment : InternalEnlistment
    {
        // This class acts as the durable single phase enlistment for a
        // promotable single phase enlistment.
        IPromotableSinglePhaseNotification promotableNotificationInterface;
 
        internal PromotableInternalEnlistment(
            Enlistment enlistment,
            InternalTransaction transaction, 
            IPromotableSinglePhaseNotification promotableSinglePhaseNotification,
            Transaction atomicTransaction
            ) :
            base(enlistment, transaction, atomicTransaction)
        {
            this.promotableNotificationInterface = promotableSinglePhaseNotification;
        }
 
 
        internal override IPromotableSinglePhaseNotification PromotableSinglePhaseNotification
        {
            get
            {
                return this.promotableNotificationInterface;
            }
        }
    }
 
 
    // This class supports volatile enlistments
    //
    internal class Phase1VolatileEnlistment : InternalEnlistment
    {
        public Phase1VolatileEnlistment(
            Enlistment enlistment,
            InternalTransaction transaction, 
            IEnlistmentNotification twoPhaseNotifications,
            ISinglePhaseNotification singlePhaseNotifications,
            Transaction atomicTransaction
            )
            : base(enlistment, transaction, twoPhaseNotifications, singlePhaseNotifications, atomicTransaction)
        {
        }
 
 
        internal override void FinishEnlistment()
        {
            // Note another enlistment finished.
            this.transaction.phase1Volatiles.preparedVolatileEnlistments++;
            CheckComplete();
        }
 
 
        internal override void CheckComplete()
        {
            // Make certain we increment the right list.
            Debug.Assert(this.transaction.phase1Volatiles.preparedVolatileEnlistments <= 
                this.transaction.phase1Volatiles.volatileEnlistmentCount +
                this.transaction.phase1Volatiles.dependentClones);
 
            // Check to see if all of the volatile enlistments are done.
            if (this.transaction.phase1Volatiles.preparedVolatileEnlistments == 
                this.transaction.phase1Volatiles.volatileEnlistmentCount + 
                this.transaction.phase1Volatiles.dependentClones)
            {
                this.transaction.State.Phase1VolatilePrepareDone(this.transaction);
            }
        }
    }
 
 
    public class Enlistment
    {
        // Interface for communicating with the state machine.
        internal InternalEnlistment internalEnlistment;
 
        internal Enlistment(
            InternalEnlistment internalEnlistment
            )
        {
            this.internalEnlistment = internalEnlistment;
        }
 
 
        internal Enlistment(
            Guid resourceManagerIdentifier,
            InternalTransaction transaction, 
            IEnlistmentNotification twoPhaseNotifications,
            ISinglePhaseNotification singlePhaseNotifications,
            Transaction atomicTransaction
            )
        {
            this.internalEnlistment = new DurableInternalEnlistment(
                this,
                resourceManagerIdentifier,
                transaction,
                twoPhaseNotifications,
                singlePhaseNotifications,
                atomicTransaction
                );
        }
 
 
        internal Enlistment(
            InternalTransaction transaction, 
            IEnlistmentNotification twoPhaseNotifications,
            ISinglePhaseNotification singlePhaseNotifications,
            Transaction atomicTransaction,
            EnlistmentOptions enlistmentOptions
            )
        {
            if ((enlistmentOptions & EnlistmentOptions.EnlistDuringPrepareRequired) != 0)
            {
                this.internalEnlistment = new InternalEnlistment(
                    this,
                    transaction,
                    twoPhaseNotifications,
                    singlePhaseNotifications,
                    atomicTransaction
                    );
            }
            else
            {
                    this.internalEnlistment = new Phase1VolatileEnlistment(
                    this,
                    transaction,
                    twoPhaseNotifications,
                    singlePhaseNotifications,
                    atomicTransaction
                    );
            }
        }
 
 
        // This constructor is for a promotable single phase enlistment.
        internal Enlistment(
            InternalTransaction transaction, 
            IPromotableSinglePhaseNotification promotableSinglePhaseNotification,
            Transaction atomicTransaction
            )
        {
            this.internalEnlistment = new PromotableInternalEnlistment(
                this,
                transaction,
                promotableSinglePhaseNotification,
                atomicTransaction
                );
        }
 
 
        internal Enlistment(
            IEnlistmentNotification twoPhaseNotifications,
            InternalTransaction transaction,
            Transaction atomicTransaction
            )
        {
            this.internalEnlistment = new InternalEnlistment(
                this,
                twoPhaseNotifications,
                transaction,
                atomicTransaction
                );
        }
 
 
        internal Enlistment(
            IEnlistmentNotification twoPhaseNotifications,
            object syncRoot
            )
        {
            this.internalEnlistment = new RecoveringInternalEnlistment(
                this,
                twoPhaseNotifications,
                syncRoot
                );
        }
 
 
        public void Done()
        {
            if (DiagnosticTrace.Verbose)
            {
                MethodEnteredTraceRecord.Trace(SR.GetString(SR.TraceSourceLtm),
                    "Enlistment.Done"
                    );
                EnlistmentCallbackPositiveTraceRecord.Trace(SR.GetString(SR.TraceSourceLtm),
                    this.internalEnlistment.EnlistmentTraceId,
                    EnlistmentCallback.Done
                    );
            }
 
            lock (this.internalEnlistment.SyncRoot)
            {
                this.internalEnlistment.State.EnlistmentDone(this.internalEnlistment);
            }
 
            if (DiagnosticTrace.Verbose)
            {
                MethodExitedTraceRecord.Trace(SR.GetString(SR.TraceSourceLtm),
                    "Enlistment.Done"
                    );
            }
        }
 
 
        internal InternalEnlistment InternalEnlistment
        {
            get
            {
                return this.internalEnlistment;
            }
        }
    }
}