internal override void EnterState(InternalEnlistment enlistment)
        {
            bool spcCommitted = false;

            // Set the enlistment state
            enlistment.State = this;

            // Send Single Phase Commit to the enlistment
            TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log;

            if (etwLog.IsEnabled())
            {
                etwLog.EnlistmentStatus(enlistment, NotificationCall.SinglePhaseCommit);
            }

            Monitor.Exit(enlistment.Transaction);
            try // Don't hold this lock while calling into the application code.
            {
                enlistment.SinglePhaseNotification.SinglePhaseCommit(enlistment.SinglePhaseEnlistment);
                spcCommitted = true;
            }
            finally
            {
                if (!spcCommitted)
                {
                    //If we have an exception thrown in SPC, we don't know the if the enlistment is committed or not
                    //reply indoubt
                    enlistment.SinglePhaseEnlistment.InDoubt();
                }
                Monitor.Enter(enlistment.Transaction);
            }
        }
        internal override void EnterState(InternalEnlistment enlistment)
        {
            // Set the enlistment state
            enlistment.State = this;

            Monitor.Exit(enlistment.Transaction);
            try
            {
                TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log;
                if (etwLog.IsEnabled())
                {
                    etwLog.EnlistmentStatus(enlistment, NotificationCall.Rollback);
                }

                // Send the Rollback notification to the enlistment
                if (enlistment.SinglePhaseNotification != null)
                {
                    enlistment.SinglePhaseNotification.Rollback(enlistment.SinglePhaseEnlistment);
                }
                else
                {
                    enlistment.PromotableSinglePhaseNotification.Rollback(enlistment.SinglePhaseEnlistment);
                }
            }
            finally
            {
                Monitor.Enter(enlistment.Transaction);
            }
        }
        internal override void EnterState(InternalEnlistment enlistment)
        {
            // Set the enlistment state
            enlistment.State = this;

            Monitor.Exit(enlistment.Transaction);
            try // Don't hold this lock while calling into the application code.
            {
                TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log;
                if (etwLog.IsEnabled())
                {
                    etwLog.EnlistmentStatus(enlistment, NotificationCall.Rollback);
                }

                enlistment.EnlistmentNotification.Rollback(enlistment.SinglePhaseEnlistment);
            }
            finally
            {
                Monitor.Enter(enlistment.Transaction);
            }
        }
        internal override void EnterState(InternalEnlistment enlistment)
        {
            // Set the enlistment state
            enlistment.State = this;

            Monitor.Exit(enlistment.Transaction);
            try // Don't hold this lock while calling into the application code.
            {
                TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log;
                if (etwLog.IsEnabled())
                {
                    etwLog.EnlistmentStatus(enlistment, NotificationCall.InDoubt);
                }

                // Forward the notification to the enlistment
                enlistment.EnlistmentNotification.InDoubt(enlistment.PreparingEnlistment);
            }
            finally
            {
                Monitor.Enter(enlistment.Transaction);
            }
        }
Example #5
0
        internal override void EnterState(InternalEnlistment enlistment)
        {
            // Set the enlistment state
            enlistment.State = this;

            Monitor.Exit(enlistment.Transaction);
            try // Don't hold this lock while calling into the application code.
            {
                TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log;
                if (etwLog.IsEnabled())
                {
                    etwLog.EnlistmentStatus(TraceSourceType.TraceSourceLtm, enlistment.EnlistmentTraceId, NotificationCall.Prepare);
                }

                Debug.Assert(enlistment.EnlistmentNotification != null);
                enlistment.EnlistmentNotification.Prepare(enlistment.PreparingEnlistment);
            }
            finally
            {
                Monitor.Enter(enlistment.Transaction);
            }
        }
        internal override void EnterState(InternalEnlistment enlistment)
        {
            bool spcCommitted = false;

            // Set the enlistment state
            enlistment.State = this;

            Monitor.Exit(enlistment.Transaction);
            try
            {
                TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log;
                if (etwLog.IsEnabled())
                {
                    etwLog.EnlistmentStatus(enlistment, NotificationCall.SinglePhaseCommit);
                }

                // Send the Commit notification to the enlistment
                if (enlistment.SinglePhaseNotification != null)
                {
                    enlistment.SinglePhaseNotification.SinglePhaseCommit(enlistment.SinglePhaseEnlistment);
                }
                else
                {
                    enlistment.PromotableSinglePhaseNotification.SinglePhaseCommit(enlistment.SinglePhaseEnlistment);
                }
                spcCommitted = true;
            }
            finally
            {
                if (!spcCommitted)
                {
                    enlistment.SinglePhaseEnlistment.InDoubt();
                }
                Monitor.Enter(enlistment.Transaction);
            }
        }
Example #7
0
    internal OletxEnlistment(
        IEnlistmentNotificationInternal enlistmentNotification,
        OletxTransactionStatus xactStatus,
        byte[] prepareInfoByteArray,
        OletxResourceManager oletxResourceManager)
        : base(oletxResourceManager, null)
    {
        // This will get set later by the creator of this object after it
        // has enlisted with the proxy.
        EnlistmentShim = null;
        _phase0Shim    = null;

        _canDoSinglePhase        = false;
        _iEnlistmentNotification = enlistmentNotification;
        State = OletxEnlistmentState.Active;

        // Do this before we do any tracing because it will affect the trace identifiers that we generate.
        Debug.Assert(prepareInfoByteArray != null,
                     "OletxEnlistment.ctor - null oletxTransaction without a prepareInfoByteArray");

        int prepareInfoLength = prepareInfoByteArray.Length;

        _proxyPrepareInfoByteArray = new byte[prepareInfoLength];
        Array.Copy(prepareInfoByteArray, _proxyPrepareInfoByteArray, prepareInfoLength);

        byte[] txGuidByteArray = new byte[16];
        Array.Copy(_proxyPrepareInfoByteArray, txGuidByteArray, 16);

        _transactionGuid      = new Guid(txGuidByteArray);
        TransactionGuidString = _transactionGuid.ToString();

        TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log;

        // If this is being created as part of a Reenlist and we already know the
        // outcome, then tell the application.
        switch (xactStatus)
        {
        case OletxTransactionStatus.OLETX_TRANSACTION_STATUS_ABORTED:
        {
            State = OletxEnlistmentState.Aborting;
            if (etwLog.IsEnabled())
            {
                etwLog.EnlistmentStatus(TraceSourceType.TraceSourceOleTx, InternalTraceIdentifier, NotificationCall.Rollback);
            }

            _iEnlistmentNotification.Rollback(this);
            break;
        }

        case OletxTransactionStatus.OLETX_TRANSACTION_STATUS_COMMITTED:
        {
            State = OletxEnlistmentState.Committing;
            // 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 (oletxResourceManager.ReenlistList)
            {
                oletxResourceManager.ReenlistPendingList.Add(this);
            }

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

            _iEnlistmentNotification.Commit(this);
            break;
        }

        case OletxTransactionStatus.OLETX_TRANSACTION_STATUS_PREPARED:
        {
            State = OletxEnlistmentState.Prepared;
            lock (oletxResourceManager.ReenlistList)
            {
                oletxResourceManager.ReenlistList.Add(this);
                oletxResourceManager.StartReenlistThread();
            }
            break;
        }

        default:
        {
            if (etwLog.IsEnabled())
            {
                etwLog.InternalError(SR.OletxEnlistmentUnexpectedTransactionStatus);
            }

            throw TransactionException.Create(
                      SR.OletxEnlistmentUnexpectedTransactionStatus, null, DistributedTxId);
        }
        }

        if (etwLog.IsEnabled())
        {
            etwLog.EnlistmentCreated(TraceSourceType.TraceSourceOleTx, InternalTraceIdentifier, EnlistmentType.Durable, EnlistmentOptions.None);
        }

        // Always do this last in case anything prior to this fails.
        AddToEnlistmentTable();
    }
    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;