示例#1
0
        public void SetDistributedTransactionIdentifier(IPromotableSinglePhaseNotification promotableNotification,
                                                        Guid distributedTransactionIdentifier)
        {
            TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log;

            if (etwLog.IsEnabled())
            {
                etwLog.MethodEnter(TraceSourceType.TraceSourceLtm, this);
            }

            if (Disposed)
            {
                throw new ObjectDisposedException(nameof(Transaction));
            }

            ArgumentNullException.ThrowIfNull(promotableNotification);

            if (distributedTransactionIdentifier == Guid.Empty)
            {
                throw new ArgumentException(null, nameof(distributedTransactionIdentifier));
            }

            if (_complete)
            {
                throw TransactionException.CreateTransactionCompletedException(DistributedTxId);
            }

            lock (_internalTransaction)
            {
                Debug.Assert(_internalTransaction.State != null);
                _internalTransaction.State.SetDistributedTransactionId(_internalTransaction,
                                                                       promotableNotification,
                                                                       distributedTransactionIdentifier);

                if (etwLog.IsEnabled())
                {
                    etwLog.MethodExit(TraceSourceType.TraceSourceLtm, this);
                }
                return;
            }
        }
示例#2
0
        /// <summary>
        /// Create a promotable single phase enlistment that promotes to a distributed transaction manager other than MSDTC.
        /// </summary>
        /// <param name="promotableSinglePhaseNotification">The object that implements the IPromotableSinglePhaseNotification interface.</param>
        /// <param name="promoterType">
        /// The promoter type Guid that identifies the format of the byte[] that is returned by the ITransactionPromoter.Promote
        /// call that is implemented by the IPromotableSinglePhaseNotificationObject, and thus the promoter of the transaction.
        /// </param>
        /// <returns>
        /// True if the enlistment is successful.
        ///
        /// False if the transaction already has a durable enlistment or promotable single phase enlistment or
        /// if the transaction has already promoted. In this case, the caller will need to enlist in the transaction through other
        /// means.
        ///
        /// If the Transaction.PromoterType matches the promoter type supported by the caller, then the
        /// Transaction.PromotedToken can be retrieved and used to enlist directly with the identified distributed transaction manager.
        ///
        /// How the enlistment is created with the distributed transaction manager identified by the Transaction.PromoterType
        /// is defined by that distributed transaction manager.
        /// </returns>
        public bool EnlistPromotableSinglePhase(IPromotableSinglePhaseNotification promotableSinglePhaseNotification, Guid promoterType)
        {
            TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log;

            if (etwLog.IsEnabled())
            {
                etwLog.MethodEnter(TraceSourceType.TraceSourceLtm, this);
            }

            if (Disposed)
            {
                throw new ObjectDisposedException(nameof(Transaction));
            }

            ArgumentNullException.ThrowIfNull(promotableSinglePhaseNotification);

            if (promoterType == Guid.Empty)
            {
                throw new ArgumentException(SR.PromoterTypeInvalid, nameof(promoterType));
            }

            if (_complete)
            {
                throw TransactionException.CreateTransactionCompletedException(DistributedTxId);
            }

            bool succeeded = false;

            lock (_internalTransaction)
            {
                Debug.Assert(_internalTransaction.State != null);
                succeeded = _internalTransaction.State.EnlistPromotableSinglePhase(_internalTransaction, promotableSinglePhaseNotification, this, promoterType);
            }

            if (etwLog.IsEnabled())
            {
                etwLog.MethodExit(TraceSourceType.TraceSourceLtm, this);
            }

            return(succeeded);
        }
示例#3
0
        // Forward request to the state machine to take the appropriate action.
        //
        public Enlistment EnlistVolatile(ISinglePhaseNotification singlePhaseNotification, EnlistmentOptions enlistmentOptions)
        {
            TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log;

            if (etwLog.IsEnabled())
            {
                etwLog.MethodEnter(TraceSourceType.TraceSourceLtm, this);
            }

            if (Disposed)
            {
                throw new ObjectDisposedException(nameof(Transaction));
            }

            if (singlePhaseNotification == null)
            {
                throw new ArgumentNullException(nameof(singlePhaseNotification));
            }

            if (enlistmentOptions != EnlistmentOptions.None && enlistmentOptions != EnlistmentOptions.EnlistDuringPrepareRequired)
            {
                throw new ArgumentOutOfRangeException(nameof(enlistmentOptions));
            }

            if (_complete)
            {
                throw TransactionException.CreateTransactionCompletedException(DistributedTxId);
            }

            lock (_internalTransaction)
            {
                Enlistment enlistment = _internalTransaction.State.EnlistVolatile(_internalTransaction,
                                                                                  singlePhaseNotification, enlistmentOptions, this);

                if (etwLog.IsEnabled())
                {
                    etwLog.MethodExit(TraceSourceType.TraceSourceLtm, this);
                }
                return(enlistment);
            }
        }
        public static IDtcTransaction GetDtcTransaction(Transaction transaction)
        {
            ArgumentNullException.ThrowIfNull(transaction);

            TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log;

            if (etwLog.IsEnabled())
            {
                etwLog.MethodEnter(TraceSourceType.TraceSourceOleTx, "TransactionInterop.GetDtcTransaction");
            }

            ConvertToOletxTransaction(transaction);
            IDtcTransaction transactionNative = OletxTransaction.GetDtcTransaction();

            if (etwLog.IsEnabled())
            {
                etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, "TransactionInterop.GetDtcTransaction");
            }

            return(transactionNative);
        }
        public static byte[] GetTransmitterPropagationToken(Transaction transaction)
        {
            ArgumentNullException.ThrowIfNull(transaction);

            TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log;

            if (etwLog.IsEnabled())
            {
                etwLog.MethodEnter(TraceSourceType.TraceSourceOleTx, "TransactionInterop.GetTransmitterPropagationToken");
            }

            ConvertToOletxTransaction(transaction);
            byte[] token = OletxTransaction.GetTransmitterPropagationToken();

            if (etwLog.IsEnabled())
            {
                etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, "TransactionInterop.GetTransmitterPropagationToken");
            }

            return(token);
        }
        public void Complete()
        {
            TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log;

            if (etwLog.IsEnabled())
            {
                etwLog.MethodEnter(TraceSourceType.TraceSourceLtm, this);
            }

            lock (_internalTransaction)
            {
                if (Disposed)
                {
                    throw new ObjectDisposedException(nameof(DependentTransaction));
                }

                if (_complete)
                {
                    throw TransactionException.CreateTransactionCompletedException(DistributedTxId);
                }

                _complete = true;

                Debug.Assert(_internalTransaction.State != null);
                if (_blocking)
                {
                    _internalTransaction.State.CompleteBlockingClone(_internalTransaction);
                }
                else
                {
                    _internalTransaction.State.CompleteAbortingClone(_internalTransaction);
                }
            }

            if (etwLog.IsEnabled())
            {
                etwLog.TransactionDependentCloneComplete(this, "DependentTransaction");
                etwLog.MethodExit(TraceSourceType.TraceSourceLtm, this);
            }
        }
示例#7
0
        // Forward the commit to the state machine to take the appropriate action.
        //
        public void Commit()
        {
            TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log;

            if (etwLog.IsEnabled())
            {
                etwLog.MethodEnter(TraceSourceType.TraceSourceLtm, this);
                etwLog.TransactionCommit(this, "CommittableTransaction");
            }

            ObjectDisposedException.ThrowIf(Disposed, this);

            lock (_internalTransaction)
            {
                if (_complete)
                {
                    throw TransactionException.CreateTransactionCompletedException(DistributedTxId);
                }

                Debug.Assert(_internalTransaction.State != null);
                _internalTransaction.State.BeginCommit(_internalTransaction, false, null, null);

                // now that commit has started wait for the monitor on the transaction to know
                // if the transaction is done.
                do
                {
                    if (_internalTransaction.State.IsCompleted(_internalTransaction))
                    {
                        break;
                    }
                } while (Monitor.Wait(_internalTransaction));

                _internalTransaction.State.EndCommit(_internalTransaction);
            }

            if (etwLog.IsEnabled())
            {
                etwLog.MethodExit(TraceSourceType.TraceSourceLtm, this);
            }
        }
        public static Transaction GetTransactionFromDtcTransaction(IDtcTransaction transactionNative)
        {
            if (null == transactionNative)
            {
                throw new ArgumentNullException(nameof(transactionNative));
            }

            TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log;

            if (etwLog.IsEnabled())
            {
                etwLog.MethodEnter(TraceSourceType.TraceSourceDistributed, "TransactionInterop.GetTransactionFromDtcTransaction");
            }

            Transaction transaction = DistributedTransactionManager.GetTransactionFromDtcTransaction(transactionNative);

            if (etwLog.IsEnabled())
            {
                etwLog.MethodExit(TraceSourceType.TraceSourceDistributed, "TransactionInterop.GetTransactionFromDtcTransaction");
            }
            return(transaction);
        }
示例#9
0
        public static void RecoveryComplete(Guid resourceManagerIdentifier)
        {
            if (resourceManagerIdentifier == Guid.Empty)
            {
                throw new ArgumentException(SR.BadResourceManagerId, nameof(resourceManagerIdentifier));
            }

            TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log;

            if (etwLog.IsEnabled())
            {
                etwLog.MethodEnter(TraceSourceType.TraceSourceBase, "TransactionManager.RecoveryComplete");
                etwLog.TransactionManagerRecoveryComplete(resourceManagerIdentifier);
            }

            DistributedTransactionManager.ResourceManagerRecoveryComplete(resourceManagerIdentifier);

            if (etwLog.IsEnabled())
            {
                etwLog.MethodExit(TraceSourceType.TraceSourceBase, "TransactionManager.RecoveryComplete");
            }
        }
    internal void BeginCommit(InternalTransaction internalTransaction)
    {
        TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log;

        if (etwLog.IsEnabled())
        {
            etwLog.MethodEnter(TraceSourceType.TraceSourceOleTx, this);
            etwLog.TransactionCommit(TraceSourceType.TraceSourceOleTx, TransactionTraceId, "CommittableTransaction");
        }

        Debug.Assert(0 == Disposed, "OletxTransction object is disposed");
        RealOletxTransaction.InternalTransaction = internalTransaction;

        _commitCalled = true;

        RealOletxTransaction.Commit();

        if (etwLog.IsEnabled())
        {
            etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, this, $"{nameof(OletxCommittableTransaction)}.{nameof(BeginCommit)}");
        }
    }
示例#11
0
        public static byte[] GetTransmitterPropagationToken(Transaction transaction)
        {
            ArgumentNullException.ThrowIfNull(transaction);

            TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log;

            if (etwLog.IsEnabled())
            {
                etwLog.MethodEnter(TraceSourceType.TraceSourceOleTx, $"{nameof(TransactionInterop)}.{nameof(GetTransmitterPropagationToken)}");
            }

            // First, make sure we are working with an OletxTransaction.
            OletxTransaction oletxTx = ConvertToOletxTransaction(transaction);

            byte[] token = GetTransmitterPropagationToken(oletxTx);

            if (etwLog.IsEnabled())
            {
                etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, $"{nameof(TransactionInterop)}.{nameof(GetTransmitterPropagationToken)}");
            }

            return(token);
        }
示例#12
0
        public void Rollback(Exception?e)
        {
            TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log;

            if (etwLog.IsEnabled())
            {
                etwLog.MethodEnter(TraceSourceType.TraceSourceLtm, this);
                etwLog.TransactionRollback(this, "Transaction");
            }

            ObjectDisposedException.ThrowIf(Disposed, this);

            lock (_internalTransaction)
            {
                Debug.Assert(_internalTransaction.State != null);
                _internalTransaction.State.Rollback(_internalTransaction, e);
            }

            if (etwLog.IsEnabled())
            {
                etwLog.MethodExit(TraceSourceType.TraceSourceLtm, this);
            }
        }
示例#13
0
        internal override void InternalDispose()
        {
            TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log;

            if (etwLog.IsEnabled())
            {
                etwLog.MethodEnter(TraceSourceType.TraceSourceLtm, this);
            }

            if (Interlocked.Exchange(ref _disposed, Transaction._disposedTrueValue) == Transaction._disposedTrueValue)
            {
                return;
            }

            Debug.Assert(_internalTransaction.State != null);
            if (_internalTransaction.State.get_Status(_internalTransaction) == TransactionStatus.Active)
            {
                lock (_internalTransaction)
                {
                    // Since this is the root transaction do state based dispose.
                    _internalTransaction.State.DisposeRoot(_internalTransaction);
                }
            }

            // Attempt to clean up the internal transaction
            long remainingITx = Interlocked.Decrement(ref _internalTransaction._cloneCount);

            if (remainingITx == 0)
            {
                _internalTransaction.Dispose();
            }

            if (etwLog.IsEnabled())
            {
                etwLog.MethodExit(TraceSourceType.TraceSourceLtm, this);
            }
        }
示例#14
0
        // Create a dependent clone of the transaction that forwards requests to this object.
        //
        public DependentTransaction DependentClone(
            DependentCloneOption cloneOption
            )
        {
            TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log;

            if (etwLog.IsEnabled())
            {
                etwLog.MethodEnter(TraceSourceType.TraceSourceLtm, this);
            }

            if (cloneOption != DependentCloneOption.BlockCommitUntilComplete &&
                cloneOption != DependentCloneOption.RollbackIfNotComplete)
            {
                throw new ArgumentOutOfRangeException(nameof(cloneOption));
            }

            if (Disposed)
            {
                throw new ObjectDisposedException(nameof(Transaction));
            }

            if (_complete)
            {
                throw TransactionException.CreateTransactionCompletedException(DistributedTxId);
            }

            DependentTransaction clone = new DependentTransaction(
                _isoLevel, _internalTransaction, cloneOption == DependentCloneOption.BlockCommitUntilComplete);

            if (etwLog.IsEnabled())
            {
                etwLog.TransactionCloneCreate(clone, "DependentTransaction");
                etwLog.MethodExit(TraceSourceType.TraceSourceLtm, this);
            }
            return(clone);
        }
示例#15
0
        // Ask the state machine for serialization info.
        //
        void ISerializable.GetObjectData(
            SerializationInfo serializationInfo,
            StreamingContext context)
        {
            TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log;

            if (etwLog.IsEnabled())
            {
                etwLog.MethodEnter(TraceSourceType.TraceSourceLtm, this);
            }

            if (Disposed)
            {
                throw new ObjectDisposedException(nameof(Transaction));
            }

            if (serializationInfo == null)
            {
                throw new ArgumentNullException(nameof(serializationInfo));
            }

            if (_complete)
            {
                throw TransactionException.CreateTransactionCompletedException(DistributedTxId);
            }

            lock (_internalTransaction)
            {
                _internalTransaction.State.GetObjectData(_internalTransaction, serializationInfo, context);
            }

            if (etwLog.IsEnabled())
            {
                etwLog.TransactionSerialized(this, "Transaction");
                etwLog.MethodExit(TraceSourceType.TraceSourceLtm, this);
            }
        }
示例#16
0
        public void InDoubt()
        {
            TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log;

            if (etwLog.IsEnabled())
            {
                etwLog.MethodEnter(TraceSourceType.TraceSourceLtm, this);
            }

            lock (_internalEnlistment.SyncRoot)
            {
                if (etwLog.IsEnabled())
                {
                    etwLog.EnlistmentInDoubt(_internalEnlistment);
                }

                _internalEnlistment.State.InDoubt(_internalEnlistment, null);
            }

            if (etwLog.IsEnabled())
            {
                etwLog.MethodExit(TraceSourceType.TraceSourceLtm, this);
            }
        }
示例#17
0
        // Create a clone of the transaction that forwards requests to this object.
        //
        public Transaction Clone()
        {
            TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log;

            if (etwLog.IsEnabled())
            {
                etwLog.MethodEnter(TraceSourceType.TraceSourceLtm, this);
            }

            ObjectDisposedException.ThrowIf(Disposed, this);

            if (_complete)
            {
                throw TransactionException.CreateTransactionCompletedException(DistributedTxId);
            }

            Transaction clone = InternalClone();

            if (etwLog.IsEnabled())
            {
                etwLog.MethodExit(TraceSourceType.TraceSourceLtm, this);
            }
            return(clone);
        }
示例#18
0
        public TransactionScope(
            Transaction transactionToUse,
            TimeSpan scopeTimeout,
            TransactionScopeAsyncFlowOption asyncFlowOption)
        {
            TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log;

            if (etwLog.IsEnabled())
            {
                etwLog.MethodEnter(TraceSourceType.TraceSourceBase, this);
            }

            ValidateAndSetAsyncFlowOption(asyncFlowOption);

            Initialize(
                transactionToUse,
                scopeTimeout,
                false);

            if (etwLog.IsEnabled())
            {
                etwLog.MethodExit(TraceSourceType.TraceSourceBase, this);
            }
        }
        public static IDtcTransaction GetDtcTransaction(Transaction transaction)
        {
            if (null == transaction)
            {
                throw new ArgumentNullException(nameof(transaction));
            }

            TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log;

            if (etwLog.IsEnabled())
            {
                etwLog.MethodEnter(TraceSourceType.TraceSourceDistributed, "TransactionInterop.GetDtcTransaction");
            }

            DistributedTransaction dTx = ConvertToDistributedTransaction(transaction);
            IDtcTransaction        transactionNative = dTx.GetDtcTransaction();

            if (etwLog.IsEnabled())
            {
                etwLog.MethodExit(TraceSourceType.TraceSourceDistributed, "TransactionInterop.GetDtcTransaction");
            }

            return(transactionNative);
        }
示例#20
0
        public void Complete()
        {
            TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log;

            if (etwLog.IsEnabled())
            {
                etwLog.MethodEnter(TraceSourceType.TraceSourceBase, this);
            }
            if (_disposed)
            {
                throw new ObjectDisposedException(nameof(TransactionScope));
            }

            if (_complete)
            {
                throw TransactionException.CreateInvalidOperationException(TraceSourceType.TraceSourceBase, SR.DisposeScope, null);
            }

            _complete = true;
            if (etwLog.IsEnabled())
            {
                etwLog.MethodExit(TraceSourceType.TraceSourceBase, this);
            }
        }
        public byte[] RecoveryInformation()
        {
            TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log;

            if (etwLog.IsEnabled())
            {
                etwLog.MethodEnter(TraceSourceType.TraceSourceLtm, this);
            }

            try
            {
                lock (_internalEnlistment.SyncRoot)
                {
                    return(_internalEnlistment.State.RecoveryInformation(_internalEnlistment));
                }
            }
            finally
            {
                if (etwLog.IsEnabled())
                {
                    etwLog.MethodExit(TraceSourceType.TraceSourceLtm, this);
                }
            }
        }
示例#22
0
    internal void ReenlistThread(object?state)
    {
        int                 localLoopCount;
        bool                done;
        OletxEnlistment?    localEnlistment;
        ResourceManagerShim?localResourceManagerShim;
        bool                success;
        Timer?              localTimer        = null;
        bool                disposeLocalTimer = false;

        OletxResourceManager resourceManager = (OletxResourceManager)state !;

        try
        {
            TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log;
            if (etwLog.IsEnabled())
            {
                etwLog.MethodEnter(TraceSourceType.TraceSourceOleTx, this, $"{nameof(OletxResourceManager)}.{nameof(ReenlistThread)}");
            }

            lock (resourceManager)
            {
                localResourceManagerShim = resourceManager.ResourceManagerShim;
                localTimer = resourceManager.ReenlistThreadTimer;
                resourceManager.ReenlistThreadTimer = null;
                resourceManager.reenlistThread      = Thread.CurrentThread;
            }

            // We only want to do work if we have a resourceManagerShim.
            if (localResourceManagerShim != null)
            {
                lock (resourceManager.ReenlistList)
                {
                    // Get the current count on the list.
                    localLoopCount = resourceManager.ReenlistList.Count;
                }

                done = false;
                while (!done && localLoopCount > 0 && localResourceManagerShim != null)
                {
                    lock (resourceManager.ReenlistList)
                    {
                        localEnlistment = null;
                        localLoopCount--;
                        if (resourceManager.ReenlistList.Count == 0)
                        {
                            done = true;
                        }
                        else
                        {
                            localEnlistment = resourceManager.ReenlistList[0] as OletxEnlistment;
                            if (localEnlistment == null)
                            {
                                if (etwLog.IsEnabled())
                                {
                                    etwLog.InternalError();
                                }

                                throw TransactionException.Create(SR.InternalError, null);
                            }

                            resourceManager.ReenlistList.RemoveAt(0);
                            object syncRoot = localEnlistment;
                            lock (syncRoot)
                            {
                                if (OletxEnlistment.OletxEnlistmentState.Done == localEnlistment.State)
                                {
                                    // We may be racing with a RecoveryComplete here.  Just forget about this
                                    // enlistment.
                                    localEnlistment = null;
                                }

                                else if (OletxEnlistment.OletxEnlistmentState.Prepared != localEnlistment.State)
                                {
                                    // The app hasn't yet responded to Prepare, so we don't know
                                    // if it is indoubt or not yet.  So just re-add it to the end
                                    // of the list.
                                    resourceManager.ReenlistList.Add(localEnlistment);
                                    localEnlistment = null;
                                }
                            }
                        }
                    }

                    if (localEnlistment != null)
                    {
                        OletxTransactionOutcome localOutcome = OletxTransactionOutcome.NotKnownYet;
                        try
                        {
                            Debug.Assert(localResourceManagerShim != null, "ReenlistThread - localResourceManagerShim is null");

                            // Make sure we have a prepare info.
                            if (localEnlistment.ProxyPrepareInfoByteArray == null)
                            {
                                Debug.Assert(false, string.Format(null, "this.prepareInfoByteArray == null in RecoveryInformation()"));
                                if (etwLog.IsEnabled())
                                {
                                    etwLog.InternalError();
                                }

                                throw TransactionException.Create(SR.InternalError, null);
                            }

                            localResourceManagerShim.Reenlist(localEnlistment.ProxyPrepareInfoByteArray, out localOutcome);

                            if (localOutcome == OletxTransactionOutcome.NotKnownYet)
                            {
                                object syncRoot = localEnlistment;
                                lock (syncRoot)
                                {
                                    if (OletxEnlistment.OletxEnlistmentState.Done == localEnlistment.State)
                                    {
                                        // We may be racing with a RecoveryComplete here.  Just forget about this
                                        // enlistment.
                                        localEnlistment = null;
                                    }
                                    else
                                    {
                                        // Put the enlistment back on the end of the list for retry later.
                                        lock (resourceManager.ReenlistList)
                                        {
                                            resourceManager.ReenlistList.Add(localEnlistment);
                                            localEnlistment = null;
                                        }
                                    }
                                }
                            }
                        }
                        catch (COMException ex) when(ex.ErrorCode == OletxHelper.XACT_E_CONNECTION_DOWN)
                        {
                            if (etwLog.IsEnabled())
                            {
                                etwLog.ExceptionConsumed(TraceSourceType.TraceSourceOleTx, ex);
                            }

                            // Release the resource manager so we can create a new one.
                            resourceManager.ResourceManagerShim = null;

                            // Now create a new resource manager with the proxy.
                            localResourceManagerShim = resourceManager.ResourceManagerShim;
                        }

                        // If we get here and we still have localEnlistment, then we got the outcome.
                        if (localEnlistment != null)
                        {
                            object syncRoot = localEnlistment;
                            lock (syncRoot)
                            {
                                if (OletxEnlistment.OletxEnlistmentState.Done == localEnlistment.State)
                                {
                                    // We may be racing with a RecoveryComplete here.  Just forget about this
                                    // enlistment.
                                    localEnlistment = null;
                                }
                                else
                                {
                                    // We are going to send the notification to the RM.  We need to put the
                                    // enlistment on the reenlistPendingList.  We lock the reenlistList because
                                    // we have decided that is the lock that protects both lists.  The entry will
                                    // be taken off the reenlistPendingList when the enlistment has
                                    // EnlistmentDone called on it.  The enlistment will call
                                    // RemoveFromReenlistPending.
                                    lock (resourceManager.ReenlistList)
                                    {
                                        resourceManager.ReenlistPendingList.Add(localEnlistment);
                                    }

                                    if (localOutcome == OletxTransactionOutcome.Committed)
                                    {
                                        localEnlistment.State = OletxEnlistment.OletxEnlistmentState.Committing;

                                        if (etwLog.IsEnabled())
                                        {
                                            etwLog.EnlistmentStatus(TraceSourceType.TraceSourceOleTx, localEnlistment.EnlistmentTraceId, NotificationCall.Commit);
                                        }

                                        localEnlistment.EnlistmentNotification !.Commit(localEnlistment);
                                    }
                                    else if (localOutcome == OletxTransactionOutcome.Aborted)
                                    {
                                        localEnlistment.State = OletxEnlistment.OletxEnlistmentState.Aborting;

                                        if (etwLog.IsEnabled())
                                        {
                                            etwLog.EnlistmentStatus(TraceSourceType.TraceSourceOleTx, localEnlistment.EnlistmentTraceId, NotificationCall.Rollback);
                                        }

                                        localEnlistment.EnlistmentNotification !.Rollback(localEnlistment);
                                    }
                                    else
                                    {
                                        if (etwLog.IsEnabled())
                                        {
                                            etwLog.InternalError();
                                        }

                                        throw TransactionException.Create(SR.InternalError, null);
                                    }
                                }
                            }
                        } // end of if null != localEnlistment
                    }     // end of if null != localEnlistment
                }
            }

            localResourceManagerShim = null;

            // Check to see if there is more work to do.
            lock (resourceManager.ReenlistList)
            {
                lock (resourceManager)
                {
                    // Get the current count on the list.
                    localLoopCount = resourceManager.ReenlistList.Count;
                    if (localLoopCount <= 0 && resourceManager.ReenlistPendingList.Count <= 0)
                    {
                        // No more entries on the list.  Try calling ReenlistComplete on the proxy, if
                        // appropriate.
                        // If the application has called RecoveryComplete,
                        // we are responsible for calling ReenlistComplete on the
                        // proxy.
                        success = resourceManager.CallProxyReenlistComplete();
                        if (success)
                        {
                            // Okay, the reenlist thread is done and we don't need to schedule another one.
                            disposeLocalTimer = true;
                        }
                        else
                        {
                            // We couldn't talk to the proxy to do ReenlistComplete, so schedule
                            // the thread again for 10 seconds from now.
                            resourceManager.ReenlistThreadTimer = localTimer;
                            if (!localTimer !.Change(10000, Timeout.Infinite))
                            {
                                throw TransactionException.CreateInvalidOperationException(
                                          TraceSourceType.TraceSourceLtm,
                                          SR.UnexpectedTimerFailure,
                                          null);
                            }
                        }
                    }
                    else
                    {
                        // There are still entries on the list, so they must not be
                        // resovled, yet.  Schedule the thread again in 10 seconds.
                        resourceManager.ReenlistThreadTimer = localTimer;
                        if (!localTimer !.Change(10000, Timeout.Infinite))
                        {
                            throw TransactionException.CreateInvalidOperationException(
                                      TraceSourceType.TraceSourceLtm,
                                      SR.UnexpectedTimerFailure,
                                      null);
                        }
                    }

                    resourceManager.reenlistThread = null;
                }

                if (etwLog.IsEnabled())
                {
                    etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, this, $"{nameof(OletxResourceManager)}.{nameof(ReenlistThread)}");
                }
            }
        }  // end of outer-most try
        finally
        {
            localResourceManagerShim = null;
            if (disposeLocalTimer && localTimer != null)
            {
                localTimer.Dispose();
            }
        }
    }  // end of ReenlistThread method;
示例#23
0
        public static Transaction GetTransactionFromExportCookie(byte[] cookie)
        {
            ArgumentNullException.ThrowIfNull(cookie);

            if (cookie.Length < 32)
            {
                throw new ArgumentException(SR.InvalidArgument, nameof(cookie));
            }

            TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log;

            if (etwLog.IsEnabled())
            {
                etwLog.MethodEnter(TraceSourceType.TraceSourceOleTx, $"{nameof(TransactionInterop)}.{nameof(GetTransactionFromExportCookie)}");
            }

            var cookieCopy = new byte[cookie.Length];

            Buffer.BlockCopy(cookie, 0, cookieCopy, 0, cookie.Length);
            cookie = cookieCopy;

            Transaction?    transaction;
            TransactionShim?transactionShim = null;
            Guid            txIdentifier    = Guid.Empty;
            OletxTransactionIsolationLevel oletxIsoLevel = OletxTransactionIsolationLevel.ISOLATIONLEVEL_SERIALIZABLE;
            OutcomeEnlistment?outcomeEnlistment;
            OletxTransaction? oleTx;

            // Extract the transaction guid from the propagation token to see if we already have a
            // transaction object for the transaction.
            // In a cookie, the transaction guid is preceded by a signature guid.
            var txId = new Guid(cookie.AsSpan(16, 16));

            // First check to see if there is a promoted LTM transaction with the same ID.  If there
            // is, just return that.
            transaction = TransactionManager.FindPromotedTransaction(txId);
            if (transaction != null)
            {
                if (etwLog.IsEnabled())
                {
                    etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, "TransactionInterop.GetTransactionFromExportCookie");
                }

                return(transaction);
            }

            // We need to create a new transaction
            RealOletxTransaction?   realTx  = null;
            OletxTransactionManager oletxTm = TransactionManager.DistributedTransactionManager;

            oletxTm.DtcTransactionManagerLock.AcquireReaderLock(-1);
            try
            {
                outcomeEnlistment = new OutcomeEnlistment();
                oletxTm.DtcTransactionManager.ProxyShimFactory.Import(cookie, outcomeEnlistment, out txIdentifier, out oletxIsoLevel, out transactionShim);
            }
            catch (COMException comException)
            {
                OletxTransactionManager.ProxyException(comException);

                // We are unsure of what the exception may mean.  It is possible that
                // we could get E_FAIL when trying to contact a transaction manager that is
                // being blocked by a fire wall.  On the other hand we may get a COMException
                // based on bad data.  The more common situation is that the data is fine
                // (since it is generated by Microsoft code) and the problem is with
                // communication.  So in this case we default for unknown exceptions to
                // assume that the problem is with communication.
                throw TransactionManagerCommunicationException.Create(SR.TraceSourceOletx, comException);
            }
            finally
            {
                oletxTm.DtcTransactionManagerLock.ReleaseReaderLock();
            }

            // We need to create a new RealOletxTransaction.
            realTx = new RealOletxTransaction(
                oletxTm,
                transactionShim,
                outcomeEnlistment,
                txIdentifier,
                oletxIsoLevel,
                false);

            // Now create the associated OletxTransaction.
            oleTx = new OletxTransaction(realTx);

            // If a transaction is found then FindOrCreate will Dispose the oletx
            // created.
            transaction = TransactionManager.FindOrCreatePromotedTransaction(txId, oleTx);

            if (etwLog.IsEnabled())
            {
                etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, $"{nameof(TransactionInterop)}.{nameof(GetTransactionFromExportCookie)}");
            }

            return(transaction);
        }
示例#24
0
        public static Transaction GetTransactionFromDtcTransaction(IDtcTransaction transactionNative)
        {
            ArgumentNullException.ThrowIfNull(transactionNative, nameof(transactionNative));

            TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log;

            if (etwLog.IsEnabled())
            {
                etwLog.MethodEnter(TraceSourceType.TraceSourceOleTx, $"{nameof(TransactionInterop)}.{nameof(GetTransactionFromDtcTransaction)}");
            }

            Transaction?    transaction     = null;
            bool            tooLate         = false;
            TransactionShim?transactionShim = null;
            Guid            txIdentifier    = Guid.Empty;
            OletxTransactionIsolationLevel oletxIsoLevel = OletxTransactionIsolationLevel.ISOLATIONLEVEL_SERIALIZABLE;
            OutcomeEnlistment?   outcomeEnlistment       = null;
            RealOletxTransaction?realTx = null;
            OletxTransaction?    oleTx  = null;

            // Let's get the guid of the transaction from the proxy to see if we already have an object.
            if (transactionNative is not ITransaction myTransactionNative)
            {
                throw new ArgumentException(SR.InvalidArgument, nameof(transactionNative));
            }

            OletxXactTransInfo xactInfo;

            try
            {
                myTransactionNative.GetTransactionInfo(out xactInfo);
            }
            catch (COMException ex) when(ex.ErrorCode == OletxHelper.XACT_E_NOTRANSACTION)
            {
                // If we get here, the transaction has appraently already been committed or aborted.  Allow creation of the
                // OletxTransaction, but it will be marked with a status of InDoubt and attempts to get its Identifier
                // property will result in a TransactionException.
                tooLate      = true;
                xactInfo.Uow = Guid.Empty;
            }

            OletxTransactionManager oletxTm = TransactionManager.DistributedTransactionManager;

            if (!tooLate)
            {
                // First check to see if there is a promoted LTM transaction with the same ID.  If there
                // is, just return that.
                transaction = TransactionManager.FindPromotedTransaction(xactInfo.Uow);
                if (transaction != null)
                {
                    if (etwLog.IsEnabled())
                    {
                        etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, $"{nameof(TransactionInterop)}.{nameof(GetTransactionFromDtcTransaction)}");
                    }

                    return(transaction);
                }

                // We need to create a new RealOletxTransaction...
                oletxTm.DtcTransactionManagerLock.AcquireReaderLock(-1);
                try
                {
                    outcomeEnlistment = new OutcomeEnlistment();
                    oletxTm.DtcTransactionManager.ProxyShimFactory.CreateTransactionShim(
                        transactionNative,
                        outcomeEnlistment,
                        out txIdentifier,
                        out oletxIsoLevel,
                        out transactionShim);
                }
                catch (COMException comException)
                {
                    OletxTransactionManager.ProxyException(comException);
                    throw;
                }
                finally
                {
                    oletxTm.DtcTransactionManagerLock.ReleaseReaderLock();
                }

                // We need to create a new RealOletxTransaction.
                realTx = new RealOletxTransaction(
                    oletxTm,
                    transactionShim,
                    outcomeEnlistment,
                    txIdentifier,
                    oletxIsoLevel,
                    false);

                oleTx = new OletxTransaction(realTx);

                // If a transaction is found then FindOrCreate will Dispose the oletx
                // created.
                transaction = TransactionManager.FindOrCreatePromotedTransaction(xactInfo.Uow, oleTx);
            }
            else
            {
                // It was too late to do a clone of the provided ITransactionNative, so we are just going to
                // create a RealOletxTransaction without a transaction shim or outcome enlistment.
                realTx = new RealOletxTransaction(
                    oletxTm,
                    null,
                    null,
                    txIdentifier,
                    OletxTransactionIsolationLevel.ISOLATIONLEVEL_SERIALIZABLE,
                    false);

                oleTx       = new OletxTransaction(realTx);
                transaction = new Transaction(oleTx);
                TransactionManager.FireDistributedTransactionStarted(transaction);
                oleTx.SavedLtmPromotedTransaction = transaction;

                InternalTransaction.DistributedTransactionOutcome(transaction._internalTransaction, TransactionStatus.InDoubt);
            }

            if (etwLog.IsEnabled())
            {
                etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, $"{nameof(TransactionInterop)}.{nameof(GetTransactionFromDtcTransaction)}");
            }

            return(transaction);
        }
示例#25
0
        public static Enlistment Reenlist(
            Guid resourceManagerIdentifier,
            byte[] recoveryInformation,
            IEnlistmentNotification enlistmentNotification)
        {
            if (resourceManagerIdentifier == Guid.Empty)
            {
                throw new ArgumentException(SR.BadResourceManagerId, nameof(resourceManagerIdentifier));
            }

            if (null == recoveryInformation)
            {
                throw new ArgumentNullException(nameof(recoveryInformation));
            }

            if (null == enlistmentNotification)
            {
                throw new ArgumentNullException(nameof(enlistmentNotification));
            }

            TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log;

            if (etwLog.IsEnabled())
            {
                etwLog.MethodEnter(TraceSourceType.TraceSourceBase, "TransactionManager.Reenlist");
                etwLog.TransactionManagerReenlist(resourceManagerIdentifier);
            }

            // Put the recovery information into a stream.
            MemoryStream stream = new MemoryStream(recoveryInformation);
            int          recoveryInformationVersion = 0;
            string?      nodeName = null;

            byte[]? resourceManagerRecoveryInformation = null;

            try
            {
                BinaryReader reader = new BinaryReader(stream);
                recoveryInformationVersion = reader.ReadInt32();

                if (recoveryInformationVersion == TransactionManager.RecoveryInformationVersion1)
                {
                    nodeName = reader.ReadString();

                    resourceManagerRecoveryInformation = reader.ReadBytes(recoveryInformation.Length - checked ((int)stream.Position));
                }
                else
                {
                    if (etwLog.IsEnabled())
                    {
                        etwLog.TransactionExceptionTrace(TraceSourceType.TraceSourceBase, TransactionExceptionType.UnrecognizedRecoveryInformation, nameof(recoveryInformation), string.Empty);
                    }

                    throw new ArgumentException(SR.UnrecognizedRecoveryInformation, nameof(recoveryInformation));
                }
            }
            catch (EndOfStreamException e)
            {
                if (etwLog.IsEnabled())
                {
                    etwLog.TransactionExceptionTrace(TraceSourceType.TraceSourceBase, TransactionExceptionType.UnrecognizedRecoveryInformation, nameof(recoveryInformation), e.ToString());
                }
                throw new ArgumentException(SR.UnrecognizedRecoveryInformation, nameof(recoveryInformation), e);
            }
            catch (FormatException e)
            {
                if (etwLog.IsEnabled())
                {
                    etwLog.TransactionExceptionTrace(TraceSourceType.TraceSourceBase, TransactionExceptionType.UnrecognizedRecoveryInformation, nameof(recoveryInformation), e.ToString());
                }
                throw new ArgumentException(SR.UnrecognizedRecoveryInformation, nameof(recoveryInformation), e);
            }
            finally
            {
                stream.Dispose();
            }

            DistributedTransactionManager transactionManager = CheckTransactionManager(nodeName);

            // Now ask the Transaction Manager to reenlist.
            object     syncRoot    = new object();
            Enlistment returnValue = new Enlistment(enlistmentNotification, syncRoot);

            EnlistmentState.EnlistmentStatePromoted.EnterState(returnValue.InternalEnlistment);

            returnValue.InternalEnlistment.PromotedEnlistment =
                transactionManager.ReenlistTransaction(
                    resourceManagerIdentifier,
                    resourceManagerRecoveryInformation,
                    (RecoveringInternalEnlistment)returnValue.InternalEnlistment
                    );

            if (etwLog.IsEnabled())
            {
                etwLog.MethodExit(TraceSourceType.TraceSourceBase, "TransactionManager.Reenlist");
            }

            return(returnValue);
        }
示例#26
0
        // We don't have a finalizer (~TransactionScope) because all it would be able to do is try to
        // operate on other managed objects (the transaction), which is not safe to do because they may
        // already have been finalized.

        public void Dispose()
        {
            bool successful = false;

            TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log;

            if (etwLog.IsEnabled())
            {
                etwLog.MethodEnter(TraceSourceType.TraceSourceBase, this);
            }
            if (_disposed)
            {
                if (etwLog.IsEnabled())
                {
                    etwLog.MethodExit(TraceSourceType.TraceSourceBase, this);
                }
                return;
            }

            // Dispose for a scope can only be called on the thread where the scope was created.
            if ((_scopeThread != Thread.CurrentThread) && !AsyncFlowEnabled)
            {
                if (etwLog.IsEnabled())
                {
                    etwLog.InvalidOperation("TransactionScope", "InvalidScopeThread");
                }

                throw new InvalidOperationException(SR.InvalidScopeThread);
            }

            Exception exToThrow = null;

            try
            {
                // Single threaded from this point
                _disposed = true;

                // First, lets pop the "stack" of TransactionScopes and dispose each one that is above us in
                // the stack, making sure they are NOT consistent before disposing them.

                // Optimize the first lookup by getting both the actual current scope and actual current
                // transaction at the same time.
                TransactionScope actualCurrentScope = _threadContextData.CurrentScope;
                Transaction      contextTransaction = null;
                Transaction      current            = Transaction.FastGetTransaction(actualCurrentScope, _threadContextData, out contextTransaction);

                if (!Equals(actualCurrentScope))
                {
                    // Ok this is bad.  But just how bad is it.  The worst case scenario is that someone is
                    // poping scopes out of order and has placed a new transaction in the top level scope.
                    // Check for that now.
                    if (actualCurrentScope == null)
                    {
                        // Something must have gone wrong trying to clean up a bad scope
                        // stack previously.
                        // Make a best effort to abort the active transaction.
                        Transaction rollbackTransaction = _committableTransaction;
                        if (rollbackTransaction == null)
                        {
                            rollbackTransaction = _dependentTransaction;
                        }
                        Debug.Assert(rollbackTransaction != null);
                        rollbackTransaction.Rollback();

                        successful = true;
                        throw TransactionException.CreateInvalidOperationException(
                                  TraceSourceType.TraceSourceBase, SR.TransactionScopeInvalidNesting, null, rollbackTransaction.DistributedTxId);
                    }
                    // Verify that expectedCurrent is the same as the "current" current if we the interopOption value is None.
                    else if (EnterpriseServicesInteropOption.None == actualCurrentScope._interopOption)
                    {
                        if (((null != actualCurrentScope._expectedCurrent) && (!actualCurrentScope._expectedCurrent.Equals(current)))
                            ||
                            ((null != current) && (null == actualCurrentScope._expectedCurrent))
                            )
                        {
                            TransactionTraceIdentifier myId;
                            TransactionTraceIdentifier currentId;

                            if (null == current)
                            {
                                currentId = TransactionTraceIdentifier.Empty;
                            }
                            else
                            {
                                currentId = current.TransactionTraceId;
                            }

                            if (null == _expectedCurrent)
                            {
                                myId = TransactionTraceIdentifier.Empty;
                            }
                            else
                            {
                                myId = _expectedCurrent.TransactionTraceId;
                            }

                            if (etwLog.IsEnabled())
                            {
                                etwLog.TransactionScopeCurrentChanged(currentId, myId);
                            }

                            exToThrow = TransactionException.CreateInvalidOperationException(TraceSourceType.TraceSourceBase, SR.TransactionScopeIncorrectCurrent, null,
                                                                                             current == null ? Guid.Empty : current.DistributedTxId);

                            // If there is a current transaction, abort it.
                            if (null != current)
                            {
                                try
                                {
                                    current.Rollback();
                                }
                                catch (TransactionException)
                                {
                                    // we are already going to throw and exception, so just ignore this one.
                                }
                                catch (ObjectDisposedException)
                                {
                                    // Dito
                                }
                            }
                        }
                    }

                    // Now fix up the scopes
                    while (!Equals(actualCurrentScope))
                    {
                        if (null == exToThrow)
                        {
                            exToThrow = TransactionException.CreateInvalidOperationException(TraceSourceType.TraceSourceBase, SR.TransactionScopeInvalidNesting, null,
                                                                                             current == null ? Guid.Empty : current.DistributedTxId);
                        }

                        if (null == actualCurrentScope._expectedCurrent)
                        {
                            if (etwLog.IsEnabled())
                            {
                                etwLog.TransactionScopeNestedIncorrectly(TransactionTraceIdentifier.Empty);
                            }
                        }
                        else
                        {
                            if (etwLog.IsEnabled())
                            {
                                etwLog.TransactionScopeNestedIncorrectly(actualCurrentScope._expectedCurrent.TransactionTraceId);
                            }
                        }

                        actualCurrentScope._complete = false;
                        try
                        {
                            actualCurrentScope.InternalDispose();
                        }
                        catch (TransactionException)
                        {
                            // we are already going to throw an exception, so just ignore this one.
                        }

                        actualCurrentScope = _threadContextData.CurrentScope;

                        // We want to fail this scope, too, because work may have been done in one of these other
                        // nested scopes that really should have been done in my scope.
                        _complete = false;
                    }
                }
                else
                {
                    // Verify that expectedCurrent is the same as the "current" current if we the interopOption value is None.
                    // If we got here, actualCurrentScope is the same as "this".
                    if (EnterpriseServicesInteropOption.None == _interopOption)
                    {
                        if (((null != _expectedCurrent) && (!_expectedCurrent.Equals(current))) ||
                            ((null != current) && (null == _expectedCurrent))
                            )
                        {
                            TransactionTraceIdentifier myId;
                            TransactionTraceIdentifier currentId;

                            if (null == current)
                            {
                                currentId = TransactionTraceIdentifier.Empty;
                            }
                            else
                            {
                                currentId = current.TransactionTraceId;
                            }

                            if (null == _expectedCurrent)
                            {
                                myId = TransactionTraceIdentifier.Empty;
                            }
                            else
                            {
                                myId = _expectedCurrent.TransactionTraceId;
                            }

                            if (etwLog.IsEnabled())
                            {
                                etwLog.TransactionScopeCurrentChanged(currentId, myId);
                            }

                            if (null == exToThrow)
                            {
                                exToThrow = TransactionException.CreateInvalidOperationException(TraceSourceType.TraceSourceBase, SR.TransactionScopeIncorrectCurrent, null,
                                                                                                 current == null ? Guid.Empty : current.DistributedTxId);
                            }

                            // If there is a current transaction, abort it.
                            if (null != current)
                            {
                                try
                                {
                                    current.Rollback();
                                }
                                catch (TransactionException)
                                {
                                    // we are already going to throw and exception, so just ignore this one.
                                }
                                catch (ObjectDisposedException)
                                {
                                    // Dito
                                }
                            }
                            // Set consistent to false so that the subsequent call to
                            // InternalDispose below will rollback this.expectedCurrent.
                            _complete = false;
                        }
                    }
                }
                successful = true;
            }
            finally
            {
                if (!successful)
                {
                    PopScope();
                }
            }

            // No try..catch here.  Just let any exception thrown by InternalDispose go out.
            InternalDispose();

            if (null != exToThrow)
            {
                throw exToThrow;
            }

            if (etwLog.IsEnabled())
            {
                etwLog.MethodExit(TraceSourceType.TraceSourceBase, this);
            }
        }
示例#27
0
        public TransactionScope(
            TransactionScopeOption scopeOption,
            TransactionOptions transactionOptions,
            EnterpriseServicesInteropOption interopOption)
        {
            TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log;

            if (etwLog.IsEnabled())
            {
                etwLog.MethodEnter(TraceSourceType.TraceSourceBase, this);
            }

            ValidateScopeTimeout("transactionOptions.Timeout", transactionOptions.Timeout);
            TimeSpan scopeTimeout = transactionOptions.Timeout;

            transactionOptions.Timeout = TransactionManager.ValidateTimeout(transactionOptions.Timeout);
            TransactionManager.ValidateIsolationLevel(transactionOptions.IsolationLevel);

            ValidateInteropOption(interopOption);
            _interopModeSpecified = true;
            _interopOption        = interopOption;

            if (NeedToCreateTransaction(scopeOption))
            {
                _committableTransaction = new CommittableTransaction(transactionOptions);
                _expectedCurrent        = _committableTransaction.Clone();
            }
            else
            {
                if (null != _expectedCurrent)
                {
                    // If the requested IsolationLevel is stronger than that of the specified transaction, throw.
                    if ((IsolationLevel.Unspecified != transactionOptions.IsolationLevel) && (_expectedCurrent.IsolationLevel != transactionOptions.IsolationLevel))
                    {
                        throw new ArgumentException(SR.TransactionScopeIsolationLevelDifferentFromTransaction, "transactionOptions.IsolationLevel");
                    }
                }
            }

            if ((null != _expectedCurrent) && (null == _committableTransaction) && (TimeSpan.Zero != scopeTimeout))
            {
                // BUGBUG: Scopes should use a shared timer
                _scopeTimer = new Timer(
                    TimerCallback,
                    this,
                    scopeTimeout,
                    TimeSpan.Zero);
            }

            if (null == _expectedCurrent)
            {
                if (etwLog.IsEnabled())
                {
                    etwLog.TransactionScopeCreated(TransactionTraceIdentifier.Empty, TransactionScopeResult.NoTransaction);
                }
            }
            else
            {
                TransactionScopeResult scopeResult;

                if (null == _committableTransaction)
                {
                    scopeResult = TransactionScopeResult.UsingExistingCurrent;
                }
                else
                {
                    scopeResult = TransactionScopeResult.CreatedTransaction;
                }

                if (etwLog.IsEnabled())
                {
                    etwLog.TransactionScopeCreated(_expectedCurrent.TransactionTraceId, scopeResult);
                }
            }

            PushScope();

            if (etwLog.IsEnabled())
            {
                etwLog.MethodExit(TraceSourceType.TraceSourceBase, this);
            }
        }
示例#28
0
        public TransactionScope(
            TransactionScopeOption scopeOption,
            TimeSpan scopeTimeout,
            TransactionScopeAsyncFlowOption asyncFlowOption
            )
        {
            TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log;

            if (etwLog.IsEnabled())
            {
                etwLog.MethodEnter(TraceSourceType.TraceSourceBase, this);
            }

            ValidateScopeTimeout(nameof(scopeTimeout), scopeTimeout);
            TimeSpan txTimeout = TransactionManager.ValidateTimeout(scopeTimeout);

            ValidateAndSetAsyncFlowOption(asyncFlowOption);

            if (NeedToCreateTransaction(scopeOption))
            {
                _committableTransaction = new CommittableTransaction(txTimeout);
                _expectedCurrent        = _committableTransaction.Clone();
            }

            if ((null != _expectedCurrent) && (null == _committableTransaction) && (TimeSpan.Zero != scopeTimeout))
            {
                // BUGBUG: Scopes should not use individual timers
                _scopeTimer = new Timer(
                    TimerCallback,
                    this,
                    scopeTimeout,
                    TimeSpan.Zero);
            }

            if (null == _expectedCurrent)
            {
                if (etwLog.IsEnabled())
                {
                    etwLog.TransactionScopeCreated(TransactionTraceIdentifier.Empty, TransactionScopeResult.NoTransaction);
                }
            }
            else
            {
                TransactionScopeResult scopeResult;

                if (null == _committableTransaction)
                {
                    scopeResult = TransactionScopeResult.UsingExistingCurrent;
                }
                else
                {
                    scopeResult = TransactionScopeResult.CreatedTransaction;
                }

                if (etwLog.IsEnabled())
                {
                    etwLog.TransactionScopeCreated(_expectedCurrent.TransactionTraceId, scopeResult);
                }
            }

            PushScope();

            if (etwLog.IsEnabled())
            {
                etwLog.MethodExit(TraceSourceType.TraceSourceBase, this);
            }
        }
    internal static void ShimNotificationCallback(object?state, bool timeout)
    {
        // First we need to get the notification from the shim factory.
        object?enlistment2 = null;
        ShimNotificationType shimNotificationType = ShimNotificationType.None;
        bool isSinglePhase;
        bool abortingHint;

        byte[]? prepareInfoBuffer = null;

        bool holdingNotificationLock = false;

        DtcProxyShimFactory localProxyShimFactory;

        TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log;

        if (etwLog.IsEnabled())
        {
            etwLog.MethodEnter(TraceSourceType.TraceSourceOleTx, $"{nameof(OletxTransactionManager)}.{nameof(ShimNotificationCallback)}");
        }

        // This lock doesn't really protect any of our data.  It is here so that if an exception occurs
        // while calling out to the app, we get an escalation to AppDomainUnload.
        Thread.BeginCriticalRegion();
        try
        {
            do
            {
                // Take a local copy of the proxyShimFactory because if we get an RM TMDown notification,
                // we will still hold the critical section in that factory, but processing of the TMDown will
                // cause replacement of the OletxTransactionManager.proxyShimFactory.
                localProxyShimFactory = ProxyShimFactory;
                try
                {
                    Thread.BeginThreadAffinity();
                    try
                    {
                        localProxyShimFactory.GetNotification(
                            out enlistment2,
                            out shimNotificationType,
                            out isSinglePhase,
                            out abortingHint,
                            out holdingNotificationLock,
                            out prepareInfoBuffer);
                    }
                    finally
                    {
                        if (holdingNotificationLock)
                        {
                            if (enlistment2 is OletxInternalResourceManager)
                            {
                                // In this case we know that the TM has gone down and we need to exchange
                                // the native lock for a managed lock.
                                ProcessingTmDown = true;
                                Monitor.Enter(ProxyShimFactory);
                            }
                            else
                            {
                                holdingNotificationLock = false;
                            }
                            localProxyShimFactory.ReleaseNotificationLock();
                        }
                        Thread.EndThreadAffinity();
                    }

                    // If a TM down is being processed it is possible that the native lock
                    // has been exchanged for a managed lock.  In that case we need to attempt
                    // to take a lock to hold up processing more events until the TM down
                    // processing is complete.
                    if (ProcessingTmDown)
                    {
                        lock (ProxyShimFactory)
                        {
                            // We don't do any work under this lock just make sure that we
                            // can take it.
                        }
                    }

                    if (shimNotificationType != ShimNotificationType.None)
                    {
                        // Next, based on the notification type, cast the Handle accordingly and make
                        // the appropriate call on the enlistment.
                        switch (shimNotificationType)
                        {
                        case ShimNotificationType.Phase0RequestNotify:
                        {
                            if (enlistment2 is OletxPhase0VolatileEnlistmentContainer ph0VolEnlistContainer)
                            {
                                ph0VolEnlistContainer.Phase0Request(abortingHint);
                            }
                            else
                            {
                                if (enlistment2 is OletxEnlistment oletxEnlistment)
                                {
                                    oletxEnlistment.Phase0Request(abortingHint);
                                }
                                else
                                {
                                    Environment.FailFast(SR.InternalError);
                                }
                            }

                            break;
                        }

                        case ShimNotificationType.VoteRequestNotify:
                        {
                            if (enlistment2 is OletxPhase1VolatileEnlistmentContainer ph1VolEnlistContainer)
                            {
                                ph1VolEnlistContainer.VoteRequest();
                            }
                            else
                            {
                                Environment.FailFast(SR.InternalError);
                            }

                            break;
                        }

                        case ShimNotificationType.CommittedNotify:
                        {
                            if (enlistment2 is OutcomeEnlistment outcomeEnlistment)
                            {
                                outcomeEnlistment.Committed();
                            }
                            else
                            {
                                if (enlistment2 is OletxPhase1VolatileEnlistmentContainer ph1VolEnlistContainer)
                                {
                                    ph1VolEnlistContainer.Committed();
                                }
                                else
                                {
                                    Environment.FailFast(SR.InternalError);
                                }
                            }

                            break;
                        }

                        case ShimNotificationType.AbortedNotify:
                        {
                            if (enlistment2 is OutcomeEnlistment outcomeEnlistment)
                            {
                                outcomeEnlistment.Aborted();
                            }
                            else
                            {
                                if (enlistment2 is OletxPhase1VolatileEnlistmentContainer ph1VolEnlistContainer)
                                {
                                    ph1VolEnlistContainer.Aborted();
                                }
                                // else
                                // Voters may receive notifications even
                                // in cases where they therwise respond
                                // negatively to the vote request.  It is
                                // also not guaranteed that we will get a
                                // notification if we do respond negatively.
                                // The only safe thing to do is to free the
                                // Handle when we abort the transaction
                                // with a voter.  These two things together
                                // mean that we cannot guarantee that this
                                // Handle will be alive when we get this
                                // notification.
                            }

                            break;
                        }

                        case ShimNotificationType.InDoubtNotify:
                        {
                            if (enlistment2 is OutcomeEnlistment outcomeEnlistment)
                            {
                                outcomeEnlistment.InDoubt();
                            }
                            else
                            {
                                if (enlistment2 is OletxPhase1VolatileEnlistmentContainer ph1VolEnlistContainer)
                                {
                                    ph1VolEnlistContainer.InDoubt();
                                }
                                else
                                {
                                    Environment.FailFast(SR.InternalError);
                                }
                            }

                            break;
                        }

                        case ShimNotificationType.PrepareRequestNotify:
                        {
                            bool enlistmentDone = true;

                            if (enlistment2 is OletxEnlistment enlistment)
                            {
                                enlistmentDone = enlistment.PrepareRequest(isSinglePhase, prepareInfoBuffer !);
                            }
                            else
                            {
                                Environment.FailFast(SR.InternalError);
                            }

                            break;
                        }

                        case ShimNotificationType.CommitRequestNotify:
                        {
                            if (enlistment2 is OletxEnlistment enlistment)
                            {
                                enlistment.CommitRequest();
                            }
                            else
                            {
                                Environment.FailFast(SR.InternalError);
                            }

                            break;
                        }

                        case ShimNotificationType.AbortRequestNotify:
                        {
                            if (enlistment2 is OletxEnlistment enlistment)
                            {
                                enlistment.AbortRequest();
                            }
                            else
                            {
                                Environment.FailFast(SR.InternalError);
                            }

                            break;
                        }

                        case ShimNotificationType.EnlistmentTmDownNotify:
                        {
                            if (enlistment2 is OletxEnlistment enlistment)
                            {
                                enlistment.TMDown();
                            }
                            else
                            {
                                Environment.FailFast(SR.InternalError);
                            }

                            break;
                        }

                        case ShimNotificationType.ResourceManagerTmDownNotify:
                        {
                            switch (enlistment2)
                            {
                            case OletxResourceManager resourceManager:
                                resourceManager.TMDown();
                                break;

                            case OletxInternalResourceManager internalResourceManager:
                                internalResourceManager.TMDown();
                                break;

                            default:
                                Environment.FailFast(SR.InternalError);
                                break;
                            }

                            break;
                        }

                        default:
                        {
                            Environment.FailFast(SR.InternalError);
                            break;
                        }
                        }
                    }
                }
                finally
                {
                    if (holdingNotificationLock)
                    {
                        holdingNotificationLock = false;
                        ProcessingTmDown        = false;
                        Monitor.Exit(ProxyShimFactory);
                    }
                }
            }while (shimNotificationType != ShimNotificationType.None);
        }
        finally
        {
            if (holdingNotificationLock)
            {
                holdingNotificationLock = false;
                ProcessingTmDown        = false;
                Monitor.Exit(ProxyShimFactory);
            }

            Thread.EndCriticalRegion();
        }

        if (etwLog.IsEnabled())
        {
            etwLog.MethodExit(TraceSourceType.TraceSourceOleTx, $"{nameof(OletxTransactionManager)}.{nameof(ShimNotificationCallback)}");
        }
    }