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