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); } }
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); } }
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;