private void Timeout() { if ((!_complete) && (null != _expectedCurrent)) { TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; if (etwLog.IsEnabled()) { etwLog.TransactionScopeTimeout(_expectedCurrent.TransactionTraceId); } try { _expectedCurrent.Rollback(); } catch (ObjectDisposedException ex) { // Tolerate the fact that the transaction has already been disposed. if (etwLog.IsEnabled()) { etwLog.ExceptionConsumed(TraceSourceType.TraceSourceBase, ex); } } catch (TransactionException txEx) { // Tolerate transaction exceptions if (etwLog.IsEnabled()) { etwLog.ExceptionConsumed(TraceSourceType.TraceSourceBase, txEx); } } } }
protected override void InternalPrepare() { try { _transaction.State.ChangeStatePromotedPhase1(_transaction); } catch (TransactionAbortedException e) { _promotedEnlistment.ForceRollback(e); TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; if (etwLog.IsEnabled()) { etwLog.ExceptionConsumed(e); } } catch (TransactionInDoubtException e) { _promotedEnlistment.EnlistmentDone(); TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; if (etwLog.IsEnabled()) { etwLog.ExceptionConsumed(e); } } }
internal bool CallProxyReenlistComplete() { bool success = false; if (RecoveryCompleteCalledByApplication) { ResourceManagerShim?localResourceManagerShim; try { localResourceManagerShim = ResourceManagerShim; if (localResourceManagerShim != null) { localResourceManagerShim.ReenlistComplete(); success = true; } // If we don't have an iResourceManagerOletx, just tell the caller that // we weren't successful and it will schedule a retry. } catch (COMException ex) { // If we get a TMDown error, eat it and indicate that we were unsuccessful. if (ex.ErrorCode == OletxHelper.XACT_E_CONNECTION_DOWN || ex.ErrorCode == OletxHelper.XACT_E_TMNOTAVAILABLE) { success = false; TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; if (etwLog.IsEnabled()) { etwLog.ExceptionConsumed(TraceSourceType.TraceSourceOleTx, ex); } } // We might get an XACT_E_RECOVERYALREADYDONE if there are multiple OletxTransactionManager // objects for the same backend TM. We can safely ignore this error. else if (ex.ErrorCode != OletxHelper.XACT_E_RECOVERYALREADYDONE) { OletxTransactionManager.ProxyException(ex); throw; } // Getting XACT_E_RECOVERYALREADYDONE is considered success. else { success = true; } } finally { localResourceManagerShim = null; } } else // The application has not yet called RecoveryComplete, so lie just a little. { success = true; } return(success); }
internal static uint AdjustTimeout(TimeSpan timeout) { uint returnTimeout = 0; try { returnTimeout = Convert.ToUInt32(timeout.TotalMilliseconds, CultureInfo.CurrentCulture); } catch (OverflowException caughtEx) { // timeout.TotalMilliseconds might be negative, so let's catch overflow exceptions, just in case. TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; if (etwLog.IsEnabled()) { etwLog.ExceptionConsumed(TraceSourceType.TraceSourceOleTx, caughtEx); } returnTimeout = uint.MaxValue; } return(returnTimeout); }
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;
internal OletxEnlistment Reenlist(byte[] prepareInfo, IEnlistmentNotificationInternal enlistmentNotification) { OletxTransactionOutcome outcome = OletxTransactionOutcome.NotKnownYet; OletxTransactionStatus xactStatus = OletxTransactionStatus.OLETX_TRANSACTION_STATUS_NONE; if (prepareInfo == null) { throw new ArgumentException(SR.InvalidArgument, nameof(prepareInfo)); } // Verify that the resource manager guid in the recovery info matches that of the calling resource manager. byte[] rmGuidArray = new byte[16]; for (int i = 0; i < 16; i++) { rmGuidArray[i] = prepareInfo[i + 16]; } Guid rmGuid = new(rmGuidArray); if (rmGuid != ResourceManagerIdentifier) { throw TransactionException.Create(TraceSourceType.TraceSourceOleTx, SR.ResourceManagerIdDoesNotMatchRecoveryInformation, null); } // Ask the proxy resource manager to reenlist. ResourceManagerShim?localResourceManagerShim = null; try { localResourceManagerShim = ResourceManagerShim; if (localResourceManagerShim == null) { // The TM must be down. Throw the exception that will get caught below and will cause // the enlistment to start the ReenlistThread. The TMDown thread will be trying to reestablish // connection with the TM and will start the reenlist thread when it does. throw new COMException(SR.DtcTransactionManagerUnavailable, OletxHelper.XACT_E_CONNECTION_DOWN); } // Only wait for 5 milliseconds. If the TM doesn't have the outcome now, we will // put the enlistment on the reenlistList for later processing. localResourceManagerShim.Reenlist(prepareInfo, out outcome); if (OletxTransactionOutcome.Committed == outcome) { xactStatus = OletxTransactionStatus.OLETX_TRANSACTION_STATUS_COMMITTED; } else if (OletxTransactionOutcome.Aborted == outcome) { xactStatus = OletxTransactionStatus.OLETX_TRANSACTION_STATUS_ABORTED; } else // we must not know the outcome yet. { xactStatus = OletxTransactionStatus.OLETX_TRANSACTION_STATUS_PREPARED; StartReenlistThread(); } } catch (COMException ex) when(ex.ErrorCode == OletxHelper.XACT_E_CONNECTION_DOWN) { xactStatus = OletxTransactionStatus.OLETX_TRANSACTION_STATUS_PREPARED; ResourceManagerShim = null; StartReenlistThread(); TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; if (etwLog.IsEnabled()) { etwLog.ExceptionConsumed(TraceSourceType.TraceSourceOleTx, ex); } } finally { localResourceManagerShim = null; } // Now create our enlistment to tell the client the outcome. return(new OletxEnlistment(enlistmentNotification, xactStatus, prepareInfo, this)); }