Code:
/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / tx / System / Transactions / Oletx / OletxResourceManager.cs / 1305376 / OletxResourceManager.cs
using System; using System.Collections; using System.Diagnostics; using System.Globalization; using System.IO; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters.Binary; using System.Security.Permissions; using System.Threading; using System.Transactions.Diagnostics; namespace System.Transactions.Oletx { internal sealed class OletxResourceManager { internal Guid resourceManagerIdentifier; internal IResourceManagerShim resourceManagerShim; internal Hashtable enlistmentHashtable; internal static Hashtable volatileEnlistmentHashtable = new Hashtable(); internal OletxTransactionManager oletxTransactionManager; // reenlistList is a simple ArrayList of OletxEnlistment objects that are either in the // Preparing or Prepared state when we receive a TMDown notification or have had // ReenlistTransaction called for them. The ReenlistThread is responsible for traversing this // list trying to obtain the outcome for the enlistments. All access, read or write, to this // list should get a lock on the list. // Special Note: If you are going to lock both the OletxResourceManager object AND the // reenlistList, lock the reenlistList FIRST. internal ArrayList reenlistList; // reenlistPendingList is also a simple ArrayList of OletxEnlistment objects. But for these // we have received the outcome from the proxy and have called into the RM to deliver the // notification, but the RM has not yet called EnlistmentDone to let us know that the outcome // has been processed. This list must be empty, in addition to the reenlistList, in order for // the ReenlistThread to call RecoveryComplete and not be rescheduled. Access to this list // should be protected by locking the reenlistList. The lists are always accessed together, // so there is no reason to grab two locks. internal ArrayList reenlistPendingList; // This is where we keep the reenlistThread and thread timer values. If there is a reenlist thread running, // reenlistThread will be non-null. If reenlistThreadTimer is non-null, we have a timer scheduled which will // fire off a reenlist thread when it expires. Only one or the other should be non-null at a time. However, they // could both be null, which means that there is no reenlist thread running and there is no timer scheduled to // create one. Access to these members should be done only after obtaining a lock on the OletxResourceManager object. internal Timer reenlistThreadTimer; internal Thread reenlistThread; // This boolean is set to true if the resource manager application has called RecoveryComplete. // A lock on the OletxResourceManager instance will be obtained when retrieving or modifying // this value. Before calling ReenlistComplete on the DTC proxy, this value must be true. private bool recoveryCompleteCalledByApplication; internal OletxResourceManager( OletxTransactionManager transactionManager, Guid resourceManagerIdentifier ) { Debug.Assert( null != transactionManager, "Argument is null" ); // This will get set later, after the resource manager is created with the proxy. this.resourceManagerShim = null; this.oletxTransactionManager = transactionManager; this.resourceManagerIdentifier = resourceManagerIdentifier; this.enlistmentHashtable = new Hashtable(); this.reenlistList = new ArrayList(); this.reenlistPendingList = new ArrayList(); reenlistThreadTimer = null; reenlistThread = null; recoveryCompleteCalledByApplication = false; } internal IResourceManagerShim ResourceManagerShim { get { IResourceManagerShim localResourceManagerShim = null; if ( null == this.resourceManagerShim ) { lock ( this ) { if ( null == this.resourceManagerShim ) { this.oletxTransactionManager.dtcTransactionManagerLock.AcquireReaderLock( -1 ); try { Guid rmGuid = this.resourceManagerIdentifier; IntPtr handle = IntPtr.Zero; RuntimeHelpers.PrepareConstrainedRegions(); try { handle = HandleTable.AllocHandle( this ); this.oletxTransactionManager.DtcTransactionManager.ProxyShimFactory.CreateResourceManager( rmGuid, handle, out localResourceManagerShim ); } finally { if ( null == localResourceManagerShim && handle != IntPtr.Zero ) { HandleTable.FreeHandle( handle ); } } } catch( COMException ex ) { if ( ( NativeMethods.XACT_E_CONNECTION_DOWN == ex.ErrorCode ) || ( NativeMethods.XACT_E_TMNOTAVAILABLE == ex.ErrorCode ) ) { // Just to make sure... localResourceManagerShim = null; if ( DiagnosticTrace.Verbose ) { ExceptionConsumedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), ex ); } } else { throw; } } catch ( TransactionException ex ) { COMException comEx = ex.InnerException as COMException; if ( null != comEx ) { // Tolerate TM down. if ( ( NativeMethods.XACT_E_CONNECTION_DOWN == comEx.ErrorCode ) || ( NativeMethods.XACT_E_TMNOTAVAILABLE == comEx.ErrorCode ) ) { // Just to make sure... localResourceManagerShim = null; if ( DiagnosticTrace.Verbose ) { ExceptionConsumedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), ex ); } } else { throw; } } else { throw; } } finally { this.oletxTransactionManager.dtcTransactionManagerLock.ReleaseReaderLock(); } Thread.MemoryBarrier(); this.resourceManagerShim = localResourceManagerShim; } } } return this.resourceManagerShim; } set { Debug.Assert( null == value, "set_ResourceManagerShim, value not null" ); this.resourceManagerShim = value; } } internal bool CallProxyReenlistComplete() { bool success = false; if ( RecoveryCompleteCalledByApplication ) { IResourceManagerShim localResourceManagerShim = null; try { localResourceManagerShim = this.ResourceManagerShim; if ( null != localResourceManagerShim ) { 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 ( ( NativeMethods.XACT_E_CONNECTION_DOWN == ex.ErrorCode ) || ( NativeMethods.XACT_E_TMNOTAVAILABLE == ex.ErrorCode ) ) { success = false; if ( DiagnosticTrace.Verbose ) { ExceptionConsumedTraceRecord.Trace( SR.GetString( SR.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 ( NativeMethods.XACT_E_RECOVERYALREADYDONE != ex.ErrorCode ) { 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 bool RecoveryCompleteCalledByApplication { get { return this.recoveryCompleteCalledByApplication; } set { this.recoveryCompleteCalledByApplication = value; } } // This is called by the internal RM when it gets a TM Down notification. This routine will // tell the enlistments about the TMDown from the internal RM. The enlistments will then // decide what to do, based on their state. This is mainly to work around COMPlus bug 36760/36758, // where Phase0 enlistments get Phase0Request( abortHint = false ) when the TM goes down. We want // to try to avoid telling the application to prepare when we know the transaction will abort. // We can't do this out of the normal TMDown notification to the RM because it is too late. The // Phase0Request gets sent before the TMDown notification. internal void TMDownFromInternalRM( OletxTransactionManager oletxTM ) { Hashtable localEnlistmentHashtable = null; IDictionaryEnumerator enlistEnum = null; OletxEnlistment enlistment = null; // If the internal RM got a TMDown, we will shortly, so null out our ResourceManagerShim now. this.ResourceManagerShim = null; // Make our own copy of the hashtable of enlistments. lock( enlistmentHashtable.SyncRoot ) { localEnlistmentHashtable = (Hashtable) this.enlistmentHashtable.Clone(); } // Tell all of our enlistments that the TM went down. The proxy only // tells enlistments that are in the Prepared state, but we want our Phase0 // enlistments to know so they can avoid sending Prepare when they get a // Phase0Request - COMPlus bug 36760/36758. enlistEnum = localEnlistmentHashtable.GetEnumerator(); while ( enlistEnum.MoveNext() ) { enlistment = enlistEnum.Value as OletxEnlistment; if ( null != enlistment ) { enlistment.TMDownFromInternalRM( oletxTM ); } } } #region IResourceManagerSink public void TMDown() { // The ResourceManagerShim was already set to null by TMDownFromInternalRM, so we don't need to do it again here. // Just start the ReenlistThread. StartReenlistThread(); return; } #endregion internal OletxEnlistment EnlistDurable( OletxTransaction oletxTransaction, bool canDoSinglePhase, IEnlistmentNotificationInternal enlistmentNotification, EnlistmentOptions enlistmentOptions ) { IResourceManagerShim localResourceManagerShim = null; Debug.Assert( null != oletxTransaction, "Argument is null" ); Debug.Assert( null != enlistmentNotification, "Argument is null" ); IEnlistmentShim enlistmentShim = null; IPhase0EnlistmentShim phase0Shim = null; Guid txUow = Guid.Empty; IntPtr handlePhase0 = IntPtr.Zero; bool phase0EnlistSucceeded = false; bool undecidedEnlistmentsIncremented = false; // Create our enlistment object. OletxEnlistment enlistment = new OletxEnlistment( canDoSinglePhase, enlistmentNotification, oletxTransaction.RealTransaction.TxGuid, enlistmentOptions, this, oletxTransaction ); bool enlistmentSucceeded = false; RuntimeHelpers.PrepareConstrainedRegions(); try { if( (enlistmentOptions & EnlistmentOptions.EnlistDuringPrepareRequired) != 0 ) { RuntimeHelpers.PrepareConstrainedRegions(); try {} finally { oletxTransaction.RealTransaction.IncrementUndecidedEnlistments(); undecidedEnlistmentsIncremented = true; } } // This entire sequense needs to be executed before we can go on. lock( enlistment ) { RuntimeHelpers.PrepareConstrainedRegions(); try { // Do the enlistment on the proxy. localResourceManagerShim = this.ResourceManagerShim; if ( null == localResourceManagerShim ) { // The TM must be down. Throw the appropriate exception. throw TransactionManagerCommunicationException.Create( SR.GetString( SR.TraceSourceOletx), null ); } if ( (enlistmentOptions & EnlistmentOptions.EnlistDuringPrepareRequired) != 0 ) { // We need to create an EnlistmentNotifyShim if native threads are not allowed to enter managed code. handlePhase0 = HandleTable.AllocHandle( enlistment ); RuntimeHelpers.PrepareConstrainedRegions(); try {} finally { oletxTransaction.RealTransaction.TransactionShim.Phase0Enlist( handlePhase0, out phase0Shim ); phase0EnlistSucceeded = true; } enlistment.Phase0EnlistmentShim = phase0Shim; } enlistment.phase1Handle = HandleTable.AllocHandle( enlistment ); localResourceManagerShim.Enlist( oletxTransaction.RealTransaction.TransactionShim, enlistment.phase1Handle, out enlistmentShim ); enlistment.EnlistmentShim = enlistmentShim; } catch (COMException comException) { // There is no string mapping for XACT_E_TOOMANY_ENLISTMENTS, so we need to do it here. if ( NativeMethods.XACT_E_TOOMANY_ENLISTMENTS == comException.ErrorCode ) { throw TransactionException.Create( SR.GetString( SR.TraceSourceOletx ), SR.GetString( SR.OletxTooManyEnlistments ), comException ); } OletxTransactionManager.ProxyException( comException ); throw; } finally { if( enlistment.EnlistmentShim == null ) { // If the enlistment shim was never assigned then something blew up. // Perform some cleanup. if( handlePhase0 != IntPtr.Zero && !phase0EnlistSucceeded ) { // Only clean up the phase0 handle if the phase 0 enlistment did not succeed. // This is because the notification processing code expects it to exist. HandleTable.FreeHandle( handlePhase0 ); } if( enlistment.phase1Handle != IntPtr.Zero ) { HandleTable.FreeHandle( enlistment.phase1Handle ); } // Note this code used to call unenlist however this allows race conditions where // it is unclear if the handlePhase0 should be freed or not. The notification // thread should get a phase0Request and it will free the Handle at that point. } } } enlistmentSucceeded = true; } finally { if( !enlistmentSucceeded && ((enlistmentOptions & EnlistmentOptions.EnlistDuringPrepareRequired) != 0) && undecidedEnlistmentsIncremented ) { oletxTransaction.RealTransaction.DecrementUndecidedEnlistments(); } } return enlistment; } internal OletxEnlistment Reenlist( int prepareInfoLength, byte[] prepareInfo, IEnlistmentNotificationInternal enlistmentNotification ) { OletxTransactionOutcome outcome = OletxTransactionOutcome.NotKnownYet; OletxTransactionStatus xactStatus = OletxTransactionStatus.OLETX_TRANSACTION_STATUS_NONE; // Put the recovery information into a stream. MemoryStream stream = new MemoryStream( prepareInfo ); // First extract the OletxRecoveryInformation from the stream. IFormatter formatter = new BinaryFormatter(); OletxRecoveryInformation oletxRecoveryInformation; try { oletxRecoveryInformation = formatter.Deserialize( stream ) as OletxRecoveryInformation; } catch(SerializationException se) { throw new ArgumentException( SR.GetString( SR.InvalidArgument ), "prepareInfo", se ); } if ( null == oletxRecoveryInformation ) { throw new ArgumentException( SR.GetString( SR.InvalidArgument ), "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] = oletxRecoveryInformation.proxyRecoveryInformation[i+16]; } Guid rmGuid = new Guid( rmGuidArray ); if ( rmGuid != this.resourceManagerIdentifier ) { throw TransactionException.Create( SR.GetString( SR.TraceSourceOletx ), SR.GetString( SR.ResourceManagerIdDoesNotMatchRecoveryInformation ), null ); } // Ask the proxy resource manager to reenlist. IResourceManagerShim localResourceManagerShim = null; try { localResourceManagerShim = this.ResourceManagerShim; if ( null == localResourceManagerShim ) { // 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.GetString( SR.DtcTransactionManagerUnavailable ), NativeMethods.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( Convert.ToUInt32( oletxRecoveryInformation.proxyRecoveryInformation.Length, CultureInfo.InvariantCulture ), oletxRecoveryInformation.proxyRecoveryInformation, 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 ) { if ( NativeMethods.XACT_E_CONNECTION_DOWN == ex.ErrorCode ) { xactStatus = OletxTransactionStatus.OLETX_TRANSACTION_STATUS_PREPARED; this.ResourceManagerShim = null; StartReenlistThread(); if ( DiagnosticTrace.Verbose ) { ExceptionConsumedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), ex ); } } else { throw; } } finally { localResourceManagerShim = null; } // Now create our enlistment to tell the client the outcome. OletxEnlistment enlistment = new OletxEnlistment( enlistmentNotification, xactStatus, oletxRecoveryInformation.proxyRecoveryInformation, this ); return enlistment; } internal void RecoveryComplete() { Timer localTimer = null; // Remember that the application has called RecoveryComplete. RecoveryCompleteCalledByApplication = true; try { // Remove the OletxEnlistment objects from the reenlist list because the RM says it doesn't // have any unresolved transactions, so we don't need to keep asking and the reenlist thread can exit. // Leave the reenlistPendingList alone. If we have notifications outstanding, we still can't remove those. lock( this.reenlistList ) { // If the ReenlistThread is not running and there are no reenlistPendingList entries, we need to call ReenlistComplete ourself. lock( this ) { if ( ( 0 == this.reenlistList.Count ) && ( 0 == this.reenlistPendingList.Count ) ) { if ( null != this.reenlistThreadTimer ) { // If we have a pending reenlistThreadTimer, cancel it. We do the cancel // in the finally block to satisfy FXCop. localTimer = this.reenlistThreadTimer; this.reenlistThreadTimer = null; } // Try to tell the proxy RenlistmentComplete. bool success = CallProxyReenlistComplete(); if ( !success ) { // We are now responsible for calling RecoveryComplete. Fire up the ReenlistThread // to do it for us. StartReenlistThread(); } } else { StartReenlistThread(); } } } } finally { if ( null != localTimer ) { localTimer.Dispose(); } } return; } internal void StartReenlistThread() { // We are not going to check the reenlistList.Count. Just always start the thread. We do this because // if we get a COMException from calling ReenlistComplete, we start the reenlistThreadTimer to retry it for us // in the background. lock( this ) { // We don't need a MemoryBarrier here because all access to the reenlistThreadTimer member is done while // holding a lock on the OletxResourceManager object. if ( ( null == this.reenlistThreadTimer ) && ( null == this.reenlistThread ) ) { this.reenlistThreadTimer = new Timer( this.ReenlistThread, this, 10, Timeout.Infinite ); } } } // This routine searches the reenlistPendingList for the specified enlistment and if it finds // it, removes it from the list. An enlistment calls this routine when it is "finishing" because // the RM has called EnlistmentDone or it was InDoubt. But it only calls it if the enlistment does NOT // have a WrappedTransactionEnlistmentAsync value, indicating that it is a recovery enlistment. internal void RemoveFromReenlistPending( OletxEnlistment enlistment ) { // We lock the reenlistList because we have decided to lock that list when accessing either // the reenlistList or the reenlistPendingList. lock( reenlistList ) { // This will do a linear search of the list, but that is what we need to do because // the enlistments may change indicies while notifications are outstanding. Also, // this does not throw if the enlistment isn't on the list. reenlistPendingList.Remove( enlistment ); lock( this ) { // If we have a ReenlistThread timer and both the reenlistList and the reenlistPendingList // are empty, kick the ReenlistThread now. if ( ( null != this.reenlistThreadTimer ) && ( 0 == this.reenlistList.Count ) && ( 0 == this.reenlistPendingList.Count ) ) { if( !this.reenlistThreadTimer.Change( 0, Timeout.Infinite )) { throw TransactionException.CreateInvalidOperationException( SR.GetString( SR.TraceSourceLtm ), SR.GetString(SR.UnexpectedTimerFailure), null ); } } } } } internal void ReenlistThread( object state ) { int localLoopCount = 0; bool done = false; OletxEnlistment localEnlistment = null; IResourceManagerShim localResourceManagerShim = null; bool success = false; Timer localTimer = null; bool disposeLocalTimer = false; OletxResourceManager resourceManager = (OletxResourceManager) state; try { if ( DiagnosticTrace.Information ) { MethodEnteredTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), "OletxResourceManager.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 ( null != localResourceManagerShim ) { lock( resourceManager.reenlistList ) { // Get the current count on the list. localLoopCount = resourceManager.reenlistList.Count; } done = false; while ( !done && ( localLoopCount > 0 ) && ( null != localResourceManagerShim ) ) { lock( resourceManager.reenlistList ) { localEnlistment = null; localLoopCount--; if ( 0 == resourceManager.reenlistList.Count ) { done = true; } else { localEnlistment = resourceManager.reenlistList[0] as OletxEnlistment; if ( null == localEnlistment ) { // if ( DiagnosticTrace.Critical ) { InternalErrorTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), "" ); } throw TransactionException.Create( SR.GetString( SR.TraceSourceOletx), SR.GetString( 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 ( null != localEnlistment ) { OletxTransactionOutcome localOutcome = OletxTransactionOutcome.NotKnownYet; try { Debug.Assert( null != localResourceManagerShim, "ReenlistThread - localResourceManagerShim is null" ); // Make sure we have a prepare info. if ( null == localEnlistment.ProxyPrepareInfoByteArray ) { Debug.Assert( false, string.Format( null, "this.prepareInfoByteArray == null in RecoveryInformation()" )); if ( DiagnosticTrace.Critical ) { InternalErrorTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), "" ); } throw TransactionException.Create( SR.GetString( SR.TraceSourceOletx), SR.GetString( SR.InternalError ), null ); } localResourceManagerShim.Reenlist( (UInt32) localEnlistment.ProxyPrepareInfoByteArray.Length, localEnlistment.ProxyPrepareInfoByteArray, out localOutcome ); if ( OletxTransactionOutcome.NotKnownYet == localOutcome ) { 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 ) // or whatever exception gets thrown if we get a bad hr. { if ( NativeMethods.XACT_E_CONNECTION_DOWN == ex.ErrorCode ) { if ( DiagnosticTrace.Verbose ) { ExceptionConsumedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), ex ); } if ( NativeMethods.XACT_E_CONNECTION_DOWN == ex.ErrorCode ) { // 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; } } else { // Unexpected exception, rethrow it. throw; } } // If we get here and we still have localEnlistment, then we got the outcome. if ( null != localEnlistment ) { 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 ( OletxTransactionOutcome.Committed == localOutcome ) { localEnlistment.State = OletxEnlistment.OletxEnlistmentState.Committing; if ( DiagnosticTrace.Verbose ) { EnlistmentNotificationCallTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), localEnlistment.EnlistmentTraceId, NotificationCall.Commit ); } localEnlistment.EnlistmentNotification.Commit( localEnlistment ); } else if ( OletxTransactionOutcome.Aborted == localOutcome ) { localEnlistment.State = OletxEnlistment.OletxEnlistmentState.Aborting; if ( DiagnosticTrace.Verbose ) { EnlistmentNotificationCallTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), localEnlistment.EnlistmentTraceId, NotificationCall.Rollback ); } localEnlistment.EnlistmentNotification.Rollback( localEnlistment ); } else { if ( DiagnosticTrace.Critical ) { InternalErrorTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), "" ); } throw TransactionException.Create( SR.GetString( SR.TraceSourceOletx ), SR.GetString( 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 ( ( 0 >= localLoopCount ) && ( 0 >= resourceManager.reenlistPendingList.Count ) ) { // 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( SR.GetString( SR.TraceSourceLtm ), SR.GetString(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( SR.GetString( SR.TraceSourceLtm ), SR.GetString(SR.UnexpectedTimerFailure), null ); } } resourceManager.reenlistThread = null; } if ( DiagnosticTrace.Information ) { MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), "OletxResourceManager.ReenlistThread" ); } return; } } // end of outer-most try finally { localResourceManagerShim = null; if ( ( disposeLocalTimer ) && ( null != localTimer ) ) { localTimer.Dispose(); } } } // end of ReenlistThread method; } // This is the base class for all enlistment objects. The enlistment objects provide the callback // that is made from the application and pass it through to the proxy. abstract class OletxBaseEnlistment { protected Guid enlistmentGuid; protected OletxResourceManager oletxResourceManager; protected OletxTransaction oletxTransaction; protected string transactionGuidString; protected int enlistmentId; // this needs to be internal so it can be set from the recovery information during Reenlist. internal EnlistmentTraceIdentifier traceIdentifier; // Owning public Enlistment object protected InternalEnlistment internalEnlistment; public OletxBaseEnlistment( OletxResourceManager oletxResourceManager, OletxTransaction oletxTransaction ) { Guid resourceManagerId = Guid.Empty; enlistmentGuid = Guid.NewGuid(); this.oletxResourceManager = oletxResourceManager; this.oletxTransaction = oletxTransaction; if ( null != oletxTransaction ) { this.enlistmentId = oletxTransaction.realOletxTransaction.enlistmentCount++; this.transactionGuidString = oletxTransaction.realOletxTransaction.TxGuid.ToString(); } else { this.transactionGuidString = Guid.Empty.ToString(); } this.traceIdentifier = EnlistmentTraceIdentifier.Empty; } protected EnlistmentTraceIdentifier InternalTraceIdentifier { get { if ( EnlistmentTraceIdentifier.Empty == this.traceIdentifier ) { lock( this ) { if ( EnlistmentTraceIdentifier.Empty == this.traceIdentifier ) { Guid rmId = Guid.Empty; if ( null != oletxResourceManager ) { rmId = this.oletxResourceManager.resourceManagerIdentifier; } EnlistmentTraceIdentifier temp; if ( null != this.oletxTransaction ) { temp = new EnlistmentTraceIdentifier( rmId, oletxTransaction.TransactionTraceId, this.enlistmentId ); } else { TransactionTraceIdentifier txTraceId = new TransactionTraceIdentifier( this.transactionGuidString, 0 ); temp = new EnlistmentTraceIdentifier( rmId, txTraceId, this.enlistmentId ); } Thread.MemoryBarrier(); this.traceIdentifier = temp; } } } return this.traceIdentifier; } } protected void AddToEnlistmentTable() { lock( oletxResourceManager.enlistmentHashtable.SyncRoot ) { oletxResourceManager.enlistmentHashtable.Add( enlistmentGuid, this ); } } protected void RemoveFromEnlistmentTable() { lock( oletxResourceManager.enlistmentHashtable.SyncRoot ) { oletxResourceManager.enlistmentHashtable.Remove( enlistmentGuid ); } } } } // end of namespace // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- CompileLiteralTextParser.cs
- CodeExporter.cs
- NamedPipeAppDomainProtocolHandler.cs
- WebPartTransformerAttribute.cs
- AssociatedControlConverter.cs
- BezierSegment.cs
- MethodBody.cs
- util.cs
- TreeViewDataItemAutomationPeer.cs
- cookieexception.cs
- OnOperation.cs
- wgx_commands.cs
- PropertyGridCommands.cs
- QilTernary.cs
- X509ClientCertificateCredentialsElement.cs
- DecoratedNameAttribute.cs
- EventPrivateKey.cs
- FlatButtonAppearance.cs
- CleanUpVirtualizedItemEventArgs.cs
- OleDbConnectionFactory.cs
- ObjectDataSourceEventArgs.cs
- ConfigurationManagerInternalFactory.cs
- HtmlButton.cs
- BooleanExpr.cs
- DependencyPropertyValueSerializer.cs
- SQLBytesStorage.cs
- MessageSmuggler.cs
- GenericEnumerator.cs
- ReaderWriterLockWrapper.cs
- RuntimeVariablesExpression.cs
- StylusLogic.cs
- DefaultAsyncDataDispatcher.cs
- GenericUriParser.cs
- ExceptionUtil.cs
- InstrumentationTracker.cs
- SHA384Managed.cs
- Line.cs
- Expander.cs
- WindowsToolbarItemAsMenuItem.cs
- SqlRecordBuffer.cs
- InlineObject.cs
- Gdiplus.cs
- InkCanvasSelection.cs
- selecteditemcollection.cs
- MoveSizeWinEventHandler.cs
- PointCollectionConverter.cs
- InputLanguageManager.cs
- MsmqIntegrationAppDomainProtocolHandler.cs
- Rotation3DKeyFrameCollection.cs
- ImmComposition.cs
- DEREncoding.cs
- TypeNameHelper.cs
- TypeSystem.cs
- DataRelationPropertyDescriptor.cs
- LeftCellWrapper.cs
- HandlerFactoryWrapper.cs
- XhtmlBasicImageAdapter.cs
- UInt16.cs
- VirtualPathProvider.cs
- BinaryCommonClasses.cs
- AppDomainManager.cs
- CapabilitiesState.cs
- FileClassifier.cs
- ListDataHelper.cs
- BStrWrapper.cs
- ValidationContext.cs
- MappingModelBuildProvider.cs
- ResXDataNode.cs
- CategoryAttribute.cs
- UrlPath.cs
- WSHttpBindingCollectionElement.cs
- DataSourceNameHandler.cs
- WebPartAddingEventArgs.cs
- _ShellExpression.cs
- ValidationErrorCollection.cs
- DynamicPropertyHolder.cs
- MetadataItemEmitter.cs
- FontNameEditor.cs
- BindingOperations.cs
- TrustSection.cs
- UnicodeEncoding.cs
- TagPrefixCollection.cs
- BinaryNegotiation.cs
- ReadOnlyMetadataCollection.cs
- InstanceKeyCompleteException.cs
- FilteredXmlReader.cs
- HtmlInputRadioButton.cs
- DataColumnChangeEvent.cs
- CachedRequestParams.cs
- RuntimeIdentifierPropertyAttribute.cs
- XhtmlBasicObjectListAdapter.cs
- VisualTarget.cs
- ListBox.cs
- SafeThreadHandle.cs
- SqlUserDefinedAggregateAttribute.cs
- WindowsRichEditRange.cs
- ExceptionHandlerDesigner.cs
- NumericUpDownAccelerationCollection.cs
- XmlWriter.cs
- BaseAsyncResult.cs