Code:
/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / tx / System / Transactions / Oletx / OletxTransactionManager.cs / 1305376 / OletxTransactionManager.cs
using System; using System.Collections; using System.Collections.Specialized; using System.Diagnostics; using System.Globalization; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Security.Permissions; using System.Threading; using System.Transactions.Diagnostics; namespace System.Transactions.Oletx { internal class OletxTransactionManager { System.Transactions.IsolationLevel isolationLevelProperty; TimeSpan timeoutProperty; TransactionOptions configuredTransactionOptions = new TransactionOptions(); // Object for synchronizing access to the entire class( avoiding lock( typeof( ... )) ) private static object classSyncObject; // These have to be static because we can only add an RM with the proxy once, even if we // have multiple OletxTransactionManager instances. static internal Hashtable resourceManagerHashTable; static internal System.Threading.ReaderWriterLock resourceManagerHashTableLock; volatile static internal bool processingTmDown = false; internal ReaderWriterLock dtcTransactionManagerLock; DtcTransactionManager dtcTransactionManager; internal OletxInternalResourceManager internalResourceManager; internal static IDtcProxyShimFactory proxyShimFactory = null; internal static EventWaitHandle shimWaitHandle = null; internal static EventWaitHandle ShimWaitHandle { get { if ( null == shimWaitHandle ) { lock( ClassSyncObject ) { if ( null == shimWaitHandle ) { shimWaitHandle = new EventWaitHandle( false, EventResetMode.AutoReset ); } } } return shimWaitHandle; } } string nodeNameField; // byte[] propToken; // Method that is used within SQLCLR as the WaitOrTimerCallback for the call to // ThreadPool.RegisterWaitForSingleObject. // This is here for the DangerousGetHandle call. We need to do it. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods")] internal static void ShimNotificationCallback( object state, bool timeout ) { // First we need to get the notification from the shim factory. IntPtr enlistmentHandleIntPtr = IntPtr.Zero; ShimNotificationType shimNotificationType = ShimNotificationType.None; bool isSinglePhase = false; bool abortingHint = false; UInt32 prepareInfoSize = 0; CoTaskMemHandle prepareInfoBuffer = null; bool holdingNotificationLock = false; bool cleanExit = false; IDtcProxyShimFactory localProxyShimFactory = null; if ( DiagnosticTrace.Verbose ) { MethodEnteredTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), "OletxTransactionManager.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 = OletxTransactionManager.proxyShimFactory; try { Thread.BeginThreadAffinity(); RuntimeHelpers.PrepareConstrainedRegions(); try { localProxyShimFactory.GetNotification( out enlistmentHandleIntPtr, out shimNotificationType, out isSinglePhase, out abortingHint, out holdingNotificationLock, out prepareInfoSize, out prepareInfoBuffer ); } finally { if ( holdingNotificationLock ) { if( (HandleTable.FindHandle(enlistmentHandleIntPtr)) 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; #pragma warning disable 0618 //@ System.Threading.Monitor.Enter(OletxTransactionManager.proxyShimFactory); #pragma warning restore 0618 } 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(OletxTransactionManager.proxyShimFactory) { // We don't do any work under this lock just make sure that we // can take it. } } if ( ShimNotificationType.None != shimNotificationType ) { Object target = HandleTable.FindHandle(enlistmentHandleIntPtr); // Next, based on the notification type, cast the Handle accordingly and make // the appropriate call on the enlistment. switch ( shimNotificationType ) { case ShimNotificationType.Phase0RequestNotify : { try { OletxPhase0VolatileEnlistmentContainer ph0VolEnlistContainer = target as OletxPhase0VolatileEnlistmentContainer; if ( null != ph0VolEnlistContainer ) { DiagnosticTrace.SetActivityId( ph0VolEnlistContainer.TransactionIdentifier); //CSDMain 91509 - We now synchronize this call with the AddDependentClone call in RealOleTxTransaction ph0VolEnlistContainer.Phase0Request( abortingHint ); } else { OletxEnlistment enlistment = target as OletxEnlistment; if ( null != enlistment ) { DiagnosticTrace.SetActivityId( enlistment.TransactionIdentifier); enlistment.Phase0Request( abortingHint ); } else { Environment.FailFast( SR.GetString( SR.InternalError )); } } } finally { // We aren't going to get any more notifications on this. HandleTable.FreeHandle(enlistmentHandleIntPtr); } break; } case ShimNotificationType.VoteRequestNotify : { OletxPhase1VolatileEnlistmentContainer ph1VolEnlistContainer = target as OletxPhase1VolatileEnlistmentContainer; if ( null != ph1VolEnlistContainer ) { DiagnosticTrace.SetActivityId( ph1VolEnlistContainer.TransactionIdentifier); ph1VolEnlistContainer.VoteRequest(); } else { Environment.FailFast( SR.GetString( SR.InternalError )); } break; } case ShimNotificationType.CommittedNotify : { try { OutcomeEnlistment outcomeEnlistment = target as OutcomeEnlistment; if ( null != outcomeEnlistment ) { DiagnosticTrace.SetActivityId( outcomeEnlistment.TransactionIdentifier); outcomeEnlistment.Committed(); } else { OletxPhase1VolatileEnlistmentContainer ph1VolEnlistContainer = target as OletxPhase1VolatileEnlistmentContainer; if ( null != ph1VolEnlistContainer ) { DiagnosticTrace.SetActivityId( ph1VolEnlistContainer.TransactionIdentifier); ph1VolEnlistContainer.Committed(); } else { Environment.FailFast( SR.GetString( SR.InternalError )); } } } finally { // We aren't going to get any more notifications on this. HandleTable.FreeHandle(enlistmentHandleIntPtr); } break; } case ShimNotificationType.AbortedNotify : { try { OutcomeEnlistment outcomeEnlistment = target as OutcomeEnlistment; if ( null != outcomeEnlistment ) { DiagnosticTrace.SetActivityId( outcomeEnlistment.TransactionIdentifier); outcomeEnlistment.Aborted(); } else { OletxPhase1VolatileEnlistmentContainer ph1VolEnlistContainer = target as OletxPhase1VolatileEnlistmentContainer; if ( null != ph1VolEnlistContainer ) { DiagnosticTrace.SetActivityId( ph1VolEnlistContainer.TransactionIdentifier); 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. } } finally { // We aren't going to get any more notifications on this. HandleTable.FreeHandle(enlistmentHandleIntPtr); } break; } case ShimNotificationType.InDoubtNotify : { try { OutcomeEnlistment outcomeEnlistment = target as OutcomeEnlistment; if ( null != outcomeEnlistment ) { DiagnosticTrace.SetActivityId( outcomeEnlistment.TransactionIdentifier); outcomeEnlistment.InDoubt(); } else { OletxPhase1VolatileEnlistmentContainer ph1VolEnlistContainer = target as OletxPhase1VolatileEnlistmentContainer; if ( null != ph1VolEnlistContainer ) { DiagnosticTrace.SetActivityId( ph1VolEnlistContainer.TransactionIdentifier); ph1VolEnlistContainer.InDoubt(); } else { Environment.FailFast( SR.GetString( SR.InternalError )); } } } finally { // We aren't going to get any more notifications on this. HandleTable.FreeHandle(enlistmentHandleIntPtr); } break; } case ShimNotificationType.PrepareRequestNotify : { byte[] prepareInfo = new byte[ prepareInfoSize ]; Marshal.Copy( prepareInfoBuffer.DangerousGetHandle(), prepareInfo, 0, Convert.ToInt32(prepareInfoSize) ); bool enlistmentDone = true; try { OletxEnlistment enlistment = target as OletxEnlistment; if ( null != enlistment ) { DiagnosticTrace.SetActivityId( enlistment.TransactionIdentifier); enlistmentDone = enlistment.PrepareRequest( isSinglePhase, prepareInfo ); } else { Environment.FailFast( SR.GetString( SR.InternalError )); } } finally { if(enlistmentDone) { HandleTable.FreeHandle(enlistmentHandleIntPtr); } } break; } case ShimNotificationType.CommitRequestNotify : { try { OletxEnlistment enlistment = target as OletxEnlistment; if ( null != enlistment ) { DiagnosticTrace.SetActivityId( enlistment.TransactionIdentifier); enlistment.CommitRequest(); } else { Environment.FailFast( SR.GetString( SR.InternalError )); } } finally { // We aren't going to get any more notifications on this. HandleTable.FreeHandle(enlistmentHandleIntPtr); } break; } case ShimNotificationType.AbortRequestNotify : { try { OletxEnlistment enlistment = target as OletxEnlistment; if ( null != enlistment ) { DiagnosticTrace.SetActivityId( enlistment.TransactionIdentifier); enlistment.AbortRequest(); } else { Environment.FailFast( SR.GetString( SR.InternalError )); } } finally { // We aren't going to get any more notifications on this. HandleTable.FreeHandle(enlistmentHandleIntPtr); } break; } case ShimNotificationType.EnlistmentTmDownNotify : { try { OletxEnlistment enlistment = target as OletxEnlistment; if ( null != enlistment ) { DiagnosticTrace.SetActivityId( enlistment.TransactionIdentifier); enlistment.TMDown(); } else { Environment.FailFast( SR.GetString( SR.InternalError )); } } finally { // We aren't going to get any more notifications on this. HandleTable.FreeHandle(enlistmentHandleIntPtr); } break; } case ShimNotificationType.ResourceManagerTmDownNotify : { OletxResourceManager resourceManager = target as OletxResourceManager; try { if ( null != resourceManager ) { resourceManager.TMDown(); } else { OletxInternalResourceManager internalResourceManager = target as OletxInternalResourceManager; if ( null != internalResourceManager ) { internalResourceManager.TMDown(); } else { Environment.FailFast(SR.GetString(SR.InternalError )); } } } finally { HandleTable.FreeHandle(enlistmentHandleIntPtr); } // Note that we don't free the gchandle on the OletxResourceManager. These objects // are not going to go away. break; } default : { Environment.FailFast(SR.GetString(SR.InternalError )); break; } } } } finally { if ( null != prepareInfoBuffer ) { prepareInfoBuffer.Close(); } if ( holdingNotificationLock ) { holdingNotificationLock = false; processingTmDown = false; System.Threading.Monitor.Exit(OletxTransactionManager.proxyShimFactory); } } } while ( ShimNotificationType.None != shimNotificationType ); cleanExit = true; } finally { if ( holdingNotificationLock ) { holdingNotificationLock = false; processingTmDown = false; System.Threading.Monitor.Exit(OletxTransactionManager.proxyShimFactory); } if ( !cleanExit && enlistmentHandleIntPtr != IntPtr.Zero ) { HandleTable.FreeHandle(enlistmentHandleIntPtr); } Thread.EndCriticalRegion(); } if ( DiagnosticTrace.Verbose ) { MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), "OletxTransactionManager.ShimNotificationCallback" ); } } internal OletxTransactionManager( string nodeName ) { lock( ClassSyncObject ) { // If we have not already initialized the shim factory and started the notification // thread, do so now. if (null == OletxTransactionManager.proxyShimFactory ) { Int32 error = NativeMethods.GetNotificationFactory( OletxTransactionManager.ShimWaitHandle.SafeWaitHandle, out OletxTransactionManager.proxyShimFactory ); if ( 0 != error ) { throw TransactionException.Create( SR.GetString( SR.TraceSourceOletx ), SR.GetString( SR.UnableToGetNotificationShimFactory ), null ); } ThreadPool.UnsafeRegisterWaitForSingleObject( OletxTransactionManager.ShimWaitHandle, new WaitOrTimerCallback( OletxTransactionManager.ShimNotificationCallback ), null, -1, false ); } } this.dtcTransactionManagerLock = new ReaderWriterLock(); this.nodeNameField = nodeName; // The DTC proxy doesn't like an empty string for node name on 64-bit platforms when // running as WOW64. It treats any non-null node name as a "remote" node and turns off // the WOW64 bit, causing problems when reading the registry. So if we got on empty // string for the node name, just treat it as null. if (( null != this.nodeNameField ) && ( 0 == this.nodeNameField.Length )) { this.nodeNameField = null; } if ( DiagnosticTrace.Verbose ) { DistributedTransactionManagerCreatedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), this.GetType(), this.nodeNameField ); } // Initialize the properties from config. configuredTransactionOptions.IsolationLevel = isolationLevelProperty = TransactionManager.DefaultIsolationLevel; configuredTransactionOptions.Timeout = timeoutProperty = TransactionManager.DefaultTimeout; this.internalResourceManager = new OletxInternalResourceManager( this ); dtcTransactionManagerLock.AcquireWriterLock( -1 ); try { this.dtcTransactionManager = new DtcTransactionManager( this.nodeNameField, this ); } finally { dtcTransactionManagerLock.ReleaseWriterLock(); } if (resourceManagerHashTable == null) { resourceManagerHashTable = new Hashtable(2); resourceManagerHashTableLock = new System.Threading.ReaderWriterLock(); } } internal OletxCommittableTransaction CreateTransaction( TransactionOptions properties ) { OletxCommittableTransaction tx = null; RealOletxTransaction realTransaction = null; ITransactionShim transactionShim = null; Guid txIdentifier = Guid.Empty; OutcomeEnlistment outcomeEnlistment = null; // Demand the distributed transation permission to create one of // these. DistributedTransactionPermission txPerm = new DistributedTransactionPermission( PermissionState.Unrestricted ); txPerm.Demand(); TransactionManager.ValidateIsolationLevel( properties.IsolationLevel ); // Never create a transaction with an IsolationLevel of Unspecified. if ( IsolationLevel.Unspecified == properties.IsolationLevel ) { properties.IsolationLevel = configuredTransactionOptions.IsolationLevel; } properties.Timeout = TransactionManager.ValidateTimeout( properties.Timeout ); this.dtcTransactionManagerLock.AcquireReaderLock( -1 ); try { // OletxTransactionIsolationLevel oletxIsoLevel = OletxTransactionManager.ConvertIsolationLevel( properties.IsolationLevel ); UInt32 oletxTimeout = DtcTransactionManager.AdjustTimeout( properties.Timeout ); outcomeEnlistment = new OutcomeEnlistment(); IntPtr outcomeEnlistmentHandle = IntPtr.Zero; RuntimeHelpers.PrepareConstrainedRegions(); try { outcomeEnlistmentHandle = HandleTable.AllocHandle( outcomeEnlistment ); dtcTransactionManager.ProxyShimFactory.BeginTransaction( oletxTimeout, oletxIsoLevel, outcomeEnlistmentHandle, out txIdentifier, out transactionShim ); } catch ( COMException ex ) { OletxTransactionManager.ProxyException( ex ); throw; } finally { if ( transactionShim == null && outcomeEnlistmentHandle != IntPtr.Zero ) { HandleTable.FreeHandle( outcomeEnlistmentHandle ); } } realTransaction = new RealOletxTransaction( this, transactionShim, outcomeEnlistment, txIdentifier, oletxIsoLevel, true ); tx = new OletxCommittableTransaction( realTransaction ); if ( DiagnosticTrace.Information ) { TransactionCreatedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), tx.TransactionTraceId ); } } finally { this.dtcTransactionManagerLock.ReleaseReaderLock(); } return tx; } internal OletxEnlistment ReenlistTransaction( Guid resourceManagerIdentifier, byte[] recoveryInformation, IEnlistmentNotificationInternal enlistmentNotification ) { if ( null == recoveryInformation ) { throw new ArgumentNullException( "recoveryInformation" ); } if ( null == enlistmentNotification ) { throw new ArgumentNullException( "enlistmentNotification" ); } // Now go find the resource manager in the collection. OletxResourceManager oletxResourceManager = RegisterResourceManager( resourceManagerIdentifier ); if ( null == oletxResourceManager ) { throw new ArgumentException( SR.GetString( SR.InvalidArgument ), "resourceManagerIdentifier" ); } if( oletxResourceManager.RecoveryCompleteCalledByApplication ) { throw new InvalidOperationException( SR.GetString( SR.ReenlistAfterRecoveryComplete )); } // Now ask the resource manager to reenlist. OletxEnlistment returnValue = oletxResourceManager.Reenlist( recoveryInformation.Length, recoveryInformation, enlistmentNotification ); return returnValue; } internal void ResourceManagerRecoveryComplete( Guid resourceManagerIdentifier ) { OletxResourceManager oletxRm = RegisterResourceManager( resourceManagerIdentifier ); if( oletxRm.RecoveryCompleteCalledByApplication ) { throw new InvalidOperationException( SR.GetString( SR.DuplicateRecoveryComplete )); } oletxRm.RecoveryComplete(); } internal OletxResourceManager RegisterResourceManager( Guid resourceManagerIdentifier ) { OletxResourceManager oletxResourceManager = null; resourceManagerHashTableLock.AcquireWriterLock(-1); try { // If this resource manager has already been registered, don't register it again. oletxResourceManager = resourceManagerHashTable[ resourceManagerIdentifier ] as OletxResourceManager; if ( null != oletxResourceManager ) { return oletxResourceManager; } oletxResourceManager = new OletxResourceManager( this, resourceManagerIdentifier ); resourceManagerHashTable.Add( resourceManagerIdentifier, oletxResourceManager ); } finally { resourceManagerHashTableLock.ReleaseWriterLock(); } return oletxResourceManager; } internal string CreationNodeName { get { return nodeNameField; } } internal OletxResourceManager FindOrRegisterResourceManager( Guid resourceManagerIdentifier ) { if( resourceManagerIdentifier == Guid.Empty ) { throw new ArgumentException( SR.GetString( SR.BadResourceManagerId ), "resourceManagerIdentifier" ); } OletxResourceManager oletxResourceManager = null; resourceManagerHashTableLock.AcquireReaderLock(-1); try { oletxResourceManager = resourceManagerHashTable[ resourceManagerIdentifier ] as OletxResourceManager; } finally { resourceManagerHashTableLock.ReleaseReaderLock(); } if ( null == oletxResourceManager ) { return RegisterResourceManager( resourceManagerIdentifier); } return oletxResourceManager; } internal DtcTransactionManager DtcTransactionManager { get { if ( ( this.dtcTransactionManagerLock.IsReaderLockHeld ) || ( this.dtcTransactionManagerLock.IsWriterLockHeld ) ) { if ( null == this.dtcTransactionManager ) { throw TransactionException.Create( SR.GetString( SR.TraceSourceOletx ), SR.GetString( SR.DtcTransactionManagerUnavailable ), null ); } return this.dtcTransactionManager; } else { // Internal programming error. A reader or writer lock should be held when this property is invoked. throw TransactionException.Create ( SR.GetString( SR.TraceSourceOletx ), SR.GetString( SR.InternalError ), null ); } } } internal string NodeName { get { return this.nodeNameField; } } internal static void ProxyException( COMException comException ) { if ( ( NativeMethods.XACT_E_CONNECTION_DOWN == comException.ErrorCode ) || ( NativeMethods.XACT_E_TMNOTAVAILABLE == comException.ErrorCode ) ) { throw TransactionManagerCommunicationException.Create( SR.GetString( SR.TraceSourceOletx ), SR.GetString( SR.TransactionManagerCommunicationException ), comException ); } if (( NativeMethods.XACT_E_NETWORK_TX_DISABLED == comException.ErrorCode )) { throw TransactionManagerCommunicationException.Create( SR.GetString( SR.TraceSourceOletx ), SR.GetString( SR.NetworkTransactionsDisabled ), comException ); } // Else if the error is a transaction oriented error, throw a TransactionException else if ( ( NativeMethods.XACT_E_FIRST <= comException.ErrorCode ) && ( NativeMethods.XACT_E_LAST >= comException.ErrorCode ) ) { // Special casing XACT_E_NOTRANSACTION if ( NativeMethods.XACT_E_NOTRANSACTION == comException.ErrorCode ) { throw TransactionException.Create( SR.GetString( SR.TraceSourceOletx ), SR.GetString( SR.TransactionAlreadyOver ), comException ); } throw TransactionException.Create( SR.GetString( SR.TraceSourceOletx ), comException.Message, comException ); } } internal void ReinitializeProxy() { // This is created by the static constructor. dtcTransactionManagerLock.AcquireWriterLock( -1 ); try { if ( null != dtcTransactionManager ) { dtcTransactionManager.ReleaseProxy(); } } finally { dtcTransactionManagerLock.ReleaseWriterLock(); } } internal static OletxTransactionIsolationLevel ConvertIsolationLevel( IsolationLevel isolationLevel ) { OletxTransactionIsolationLevel retVal; switch (isolationLevel) { case IsolationLevel.Serializable : retVal = OletxTransactionIsolationLevel.ISOLATIONLEVEL_SERIALIZABLE; break; case IsolationLevel.RepeatableRead : retVal = OletxTransactionIsolationLevel.ISOLATIONLEVEL_REPEATABLEREAD; break; case IsolationLevel.ReadCommitted : retVal = OletxTransactionIsolationLevel.ISOLATIONLEVEL_READCOMMITTED; break; case IsolationLevel.ReadUncommitted : retVal = OletxTransactionIsolationLevel.ISOLATIONLEVEL_READUNCOMMITTED; break; case IsolationLevel.Chaos: retVal = OletxTransactionIsolationLevel.ISOLATIONLEVEL_CHAOS; break; case IsolationLevel.Unspecified : retVal = OletxTransactionIsolationLevel.ISOLATIONLEVEL_UNSPECIFIED; break; default : retVal = OletxTransactionIsolationLevel.ISOLATIONLEVEL_SERIALIZABLE; break; } return retVal; } internal static IsolationLevel ConvertIsolationLevelFromProxyValue( OletxTransactionIsolationLevel proxyIsolationLevel ) { IsolationLevel retVal; switch (proxyIsolationLevel) { case OletxTransactionIsolationLevel.ISOLATIONLEVEL_SERIALIZABLE : retVal = IsolationLevel.Serializable; break; case OletxTransactionIsolationLevel.ISOLATIONLEVEL_REPEATABLEREAD : retVal = IsolationLevel.RepeatableRead; break; case OletxTransactionIsolationLevel.ISOLATIONLEVEL_READCOMMITTED : retVal = IsolationLevel.ReadCommitted; break; case OletxTransactionIsolationLevel.ISOLATIONLEVEL_READUNCOMMITTED : retVal = IsolationLevel.ReadUncommitted; break; case OletxTransactionIsolationLevel.ISOLATIONLEVEL_UNSPECIFIED : retVal = IsolationLevel.Unspecified; break; case OletxTransactionIsolationLevel.ISOLATIONLEVEL_CHAOS : retVal = IsolationLevel.Chaos; break; default : retVal = IsolationLevel.Serializable; break; } return retVal; } // Helper object for static synchronization internal static object ClassSyncObject { get { if( classSyncObject == null ) { object o = new object(); Interlocked.CompareExchange( ref classSyncObject, o, null ); } return classSyncObject; } } } internal class OletxInternalResourceManager { OletxTransactionManager oletxTm; Guid myGuid; internal IResourceManagerShim resourceManagerShim = null; internal OletxInternalResourceManager( OletxTransactionManager oletxTm ) { this.oletxTm = oletxTm; this.myGuid = Guid.NewGuid(); } public void TMDown() { // Let's set ourselves up for reinitialization with the proxy by releasing our // reference to the resource manager shim, which will release its reference // to the proxy when it destructs. this.resourceManagerShim = null; // We need to look through all the transactions and tell them about // the TMDown so they can tell their Phase0VolatileEnlistmentContainers. Transaction tx = null; RealOletxTransaction realTx = null; IDictionaryEnumerator tableEnum = null; if ( DiagnosticTrace.Verbose ) { MethodEnteredTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), "OletxInternalResourceManager.TMDown" ); } // make a local copy of the hash table to avoid possible deadlocks when we lock both the global hash table // and the transaction object. Hashtable txHashTable = null; lock( TransactionManager.PromotedTransactionTable.SyncRoot ) { txHashTable = (Hashtable) TransactionManager.PromotedTransactionTable.Clone(); } // No need to lock my hashtable, nobody is going to change it. tableEnum = txHashTable.GetEnumerator(); while ( tableEnum.MoveNext() ) { WeakReference txWeakRef = (WeakReference) tableEnum.Value; if ( null != txWeakRef ) { tx = (Transaction)txWeakRef.Target; if ( null != tx ) { realTx = tx.internalTransaction.PromotedTransaction.realOletxTransaction; // Only deal with transactions owned by my OletxTm. if ( realTx.OletxTransactionManagerInstance == this.oletxTm ) { realTx.TMDown(); } } } } // Now make a local copy of the hash table of resource managers and tell each of them. This is to // deal with Durable EDPR=true (phase0) enlistments. Each RM will also get a TMDown, but it will // come AFTER the "buggy" Phase0Request with abortHint=true - COMPlus bug 36760/36758. Hashtable rmHashTable = null; if ( null != OletxTransactionManager.resourceManagerHashTable ) { OletxTransactionManager.resourceManagerHashTableLock.AcquireReaderLock( Timeout.Infinite ); try { rmHashTable = (Hashtable) OletxTransactionManager.resourceManagerHashTable.Clone(); } finally { OletxTransactionManager.resourceManagerHashTableLock.ReleaseReaderLock(); } } if ( null != rmHashTable ) { // No need to lock my hashtable, nobody is going to change it. tableEnum = rmHashTable.GetEnumerator(); while ( tableEnum.MoveNext() ) { OletxResourceManager oletxRM = (OletxResourceManager) tableEnum.Value; if ( null != oletxRM ) { // When the RM spins through its enlistments, it will need to make sure that // the enlistment is for this particular TM. oletxRM.TMDownFromInternalRM( this.oletxTm ); } } } // Now let's reinitialize the shim. this.oletxTm.dtcTransactionManagerLock.AcquireWriterLock( -1 ); try { this.oletxTm.ReinitializeProxy(); } finally { this.oletxTm.dtcTransactionManagerLock.ReleaseWriterLock(); } if ( DiagnosticTrace.Verbose ) { MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), "OletxInternalResourceManager.TMDown" ); } } internal Guid Identifier { get { return this.myGuid; } } internal void CallReenlistComplete() { this.resourceManagerShim.ReenlistComplete(); } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved. using System; using System.Collections; using System.Collections.Specialized; using System.Diagnostics; using System.Globalization; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Security.Permissions; using System.Threading; using System.Transactions.Diagnostics; namespace System.Transactions.Oletx { internal class OletxTransactionManager { System.Transactions.IsolationLevel isolationLevelProperty; TimeSpan timeoutProperty; TransactionOptions configuredTransactionOptions = new TransactionOptions(); // Object for synchronizing access to the entire class( avoiding lock( typeof( ... )) ) private static object classSyncObject; // These have to be static because we can only add an RM with the proxy once, even if we // have multiple OletxTransactionManager instances. static internal Hashtable resourceManagerHashTable; static internal System.Threading.ReaderWriterLock resourceManagerHashTableLock; volatile static internal bool processingTmDown = false; internal ReaderWriterLock dtcTransactionManagerLock; DtcTransactionManager dtcTransactionManager; internal OletxInternalResourceManager internalResourceManager; internal static IDtcProxyShimFactory proxyShimFactory = null; internal static EventWaitHandle shimWaitHandle = null; internal static EventWaitHandle ShimWaitHandle { get { if ( null == shimWaitHandle ) { lock( ClassSyncObject ) { if ( null == shimWaitHandle ) { shimWaitHandle = new EventWaitHandle( false, EventResetMode.AutoReset ); } } } return shimWaitHandle; } } string nodeNameField; // byte[] propToken; // Method that is used within SQLCLR as the WaitOrTimerCallback for the call to // ThreadPool.RegisterWaitForSingleObject. // This is here for the DangerousGetHandle call. We need to do it. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods")] internal static void ShimNotificationCallback( object state, bool timeout ) { // First we need to get the notification from the shim factory. IntPtr enlistmentHandleIntPtr = IntPtr.Zero; ShimNotificationType shimNotificationType = ShimNotificationType.None; bool isSinglePhase = false; bool abortingHint = false; UInt32 prepareInfoSize = 0; CoTaskMemHandle prepareInfoBuffer = null; bool holdingNotificationLock = false; bool cleanExit = false; IDtcProxyShimFactory localProxyShimFactory = null; if ( DiagnosticTrace.Verbose ) { MethodEnteredTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), "OletxTransactionManager.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 = OletxTransactionManager.proxyShimFactory; try { Thread.BeginThreadAffinity(); RuntimeHelpers.PrepareConstrainedRegions(); try { localProxyShimFactory.GetNotification( out enlistmentHandleIntPtr, out shimNotificationType, out isSinglePhase, out abortingHint, out holdingNotificationLock, out prepareInfoSize, out prepareInfoBuffer ); } finally { if ( holdingNotificationLock ) { if( (HandleTable.FindHandle(enlistmentHandleIntPtr)) 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; #pragma warning disable 0618 //@ System.Threading.Monitor.Enter(OletxTransactionManager.proxyShimFactory); #pragma warning restore 0618 } 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(OletxTransactionManager.proxyShimFactory) { // We don't do any work under this lock just make sure that we // can take it. } } if ( ShimNotificationType.None != shimNotificationType ) { Object target = HandleTable.FindHandle(enlistmentHandleIntPtr); // Next, based on the notification type, cast the Handle accordingly and make // the appropriate call on the enlistment. switch ( shimNotificationType ) { case ShimNotificationType.Phase0RequestNotify : { try { OletxPhase0VolatileEnlistmentContainer ph0VolEnlistContainer = target as OletxPhase0VolatileEnlistmentContainer; if ( null != ph0VolEnlistContainer ) { DiagnosticTrace.SetActivityId( ph0VolEnlistContainer.TransactionIdentifier); //CSDMain 91509 - We now synchronize this call with the AddDependentClone call in RealOleTxTransaction ph0VolEnlistContainer.Phase0Request( abortingHint ); } else { OletxEnlistment enlistment = target as OletxEnlistment; if ( null != enlistment ) { DiagnosticTrace.SetActivityId( enlistment.TransactionIdentifier); enlistment.Phase0Request( abortingHint ); } else { Environment.FailFast( SR.GetString( SR.InternalError )); } } } finally { // We aren't going to get any more notifications on this. HandleTable.FreeHandle(enlistmentHandleIntPtr); } break; } case ShimNotificationType.VoteRequestNotify : { OletxPhase1VolatileEnlistmentContainer ph1VolEnlistContainer = target as OletxPhase1VolatileEnlistmentContainer; if ( null != ph1VolEnlistContainer ) { DiagnosticTrace.SetActivityId( ph1VolEnlistContainer.TransactionIdentifier); ph1VolEnlistContainer.VoteRequest(); } else { Environment.FailFast( SR.GetString( SR.InternalError )); } break; } case ShimNotificationType.CommittedNotify : { try { OutcomeEnlistment outcomeEnlistment = target as OutcomeEnlistment; if ( null != outcomeEnlistment ) { DiagnosticTrace.SetActivityId( outcomeEnlistment.TransactionIdentifier); outcomeEnlistment.Committed(); } else { OletxPhase1VolatileEnlistmentContainer ph1VolEnlistContainer = target as OletxPhase1VolatileEnlistmentContainer; if ( null != ph1VolEnlistContainer ) { DiagnosticTrace.SetActivityId( ph1VolEnlistContainer.TransactionIdentifier); ph1VolEnlistContainer.Committed(); } else { Environment.FailFast( SR.GetString( SR.InternalError )); } } } finally { // We aren't going to get any more notifications on this. HandleTable.FreeHandle(enlistmentHandleIntPtr); } break; } case ShimNotificationType.AbortedNotify : { try { OutcomeEnlistment outcomeEnlistment = target as OutcomeEnlistment; if ( null != outcomeEnlistment ) { DiagnosticTrace.SetActivityId( outcomeEnlistment.TransactionIdentifier); outcomeEnlistment.Aborted(); } else { OletxPhase1VolatileEnlistmentContainer ph1VolEnlistContainer = target as OletxPhase1VolatileEnlistmentContainer; if ( null != ph1VolEnlistContainer ) { DiagnosticTrace.SetActivityId( ph1VolEnlistContainer.TransactionIdentifier); 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. } } finally { // We aren't going to get any more notifications on this. HandleTable.FreeHandle(enlistmentHandleIntPtr); } break; } case ShimNotificationType.InDoubtNotify : { try { OutcomeEnlistment outcomeEnlistment = target as OutcomeEnlistment; if ( null != outcomeEnlistment ) { DiagnosticTrace.SetActivityId( outcomeEnlistment.TransactionIdentifier); outcomeEnlistment.InDoubt(); } else { OletxPhase1VolatileEnlistmentContainer ph1VolEnlistContainer = target as OletxPhase1VolatileEnlistmentContainer; if ( null != ph1VolEnlistContainer ) { DiagnosticTrace.SetActivityId( ph1VolEnlistContainer.TransactionIdentifier); ph1VolEnlistContainer.InDoubt(); } else { Environment.FailFast( SR.GetString( SR.InternalError )); } } } finally { // We aren't going to get any more notifications on this. HandleTable.FreeHandle(enlistmentHandleIntPtr); } break; } case ShimNotificationType.PrepareRequestNotify : { byte[] prepareInfo = new byte[ prepareInfoSize ]; Marshal.Copy( prepareInfoBuffer.DangerousGetHandle(), prepareInfo, 0, Convert.ToInt32(prepareInfoSize) ); bool enlistmentDone = true; try { OletxEnlistment enlistment = target as OletxEnlistment; if ( null != enlistment ) { DiagnosticTrace.SetActivityId( enlistment.TransactionIdentifier); enlistmentDone = enlistment.PrepareRequest( isSinglePhase, prepareInfo ); } else { Environment.FailFast( SR.GetString( SR.InternalError )); } } finally { if(enlistmentDone) { HandleTable.FreeHandle(enlistmentHandleIntPtr); } } break; } case ShimNotificationType.CommitRequestNotify : { try { OletxEnlistment enlistment = target as OletxEnlistment; if ( null != enlistment ) { DiagnosticTrace.SetActivityId( enlistment.TransactionIdentifier); enlistment.CommitRequest(); } else { Environment.FailFast( SR.GetString( SR.InternalError )); } } finally { // We aren't going to get any more notifications on this. HandleTable.FreeHandle(enlistmentHandleIntPtr); } break; } case ShimNotificationType.AbortRequestNotify : { try { OletxEnlistment enlistment = target as OletxEnlistment; if ( null != enlistment ) { DiagnosticTrace.SetActivityId( enlistment.TransactionIdentifier); enlistment.AbortRequest(); } else { Environment.FailFast( SR.GetString( SR.InternalError )); } } finally { // We aren't going to get any more notifications on this. HandleTable.FreeHandle(enlistmentHandleIntPtr); } break; } case ShimNotificationType.EnlistmentTmDownNotify : { try { OletxEnlistment enlistment = target as OletxEnlistment; if ( null != enlistment ) { DiagnosticTrace.SetActivityId( enlistment.TransactionIdentifier); enlistment.TMDown(); } else { Environment.FailFast( SR.GetString( SR.InternalError )); } } finally { // We aren't going to get any more notifications on this. HandleTable.FreeHandle(enlistmentHandleIntPtr); } break; } case ShimNotificationType.ResourceManagerTmDownNotify : { OletxResourceManager resourceManager = target as OletxResourceManager; try { if ( null != resourceManager ) { resourceManager.TMDown(); } else { OletxInternalResourceManager internalResourceManager = target as OletxInternalResourceManager; if ( null != internalResourceManager ) { internalResourceManager.TMDown(); } else { Environment.FailFast(SR.GetString(SR.InternalError )); } } } finally { HandleTable.FreeHandle(enlistmentHandleIntPtr); } // Note that we don't free the gchandle on the OletxResourceManager. These objects // are not going to go away. break; } default : { Environment.FailFast(SR.GetString(SR.InternalError )); break; } } } } finally { if ( null != prepareInfoBuffer ) { prepareInfoBuffer.Close(); } if ( holdingNotificationLock ) { holdingNotificationLock = false; processingTmDown = false; System.Threading.Monitor.Exit(OletxTransactionManager.proxyShimFactory); } } } while ( ShimNotificationType.None != shimNotificationType ); cleanExit = true; } finally { if ( holdingNotificationLock ) { holdingNotificationLock = false; processingTmDown = false; System.Threading.Monitor.Exit(OletxTransactionManager.proxyShimFactory); } if ( !cleanExit && enlistmentHandleIntPtr != IntPtr.Zero ) { HandleTable.FreeHandle(enlistmentHandleIntPtr); } Thread.EndCriticalRegion(); } if ( DiagnosticTrace.Verbose ) { MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), "OletxTransactionManager.ShimNotificationCallback" ); } } internal OletxTransactionManager( string nodeName ) { lock( ClassSyncObject ) { // If we have not already initialized the shim factory and started the notification // thread, do so now. if (null == OletxTransactionManager.proxyShimFactory ) { Int32 error = NativeMethods.GetNotificationFactory( OletxTransactionManager.ShimWaitHandle.SafeWaitHandle, out OletxTransactionManager.proxyShimFactory ); if ( 0 != error ) { throw TransactionException.Create( SR.GetString( SR.TraceSourceOletx ), SR.GetString( SR.UnableToGetNotificationShimFactory ), null ); } ThreadPool.UnsafeRegisterWaitForSingleObject( OletxTransactionManager.ShimWaitHandle, new WaitOrTimerCallback( OletxTransactionManager.ShimNotificationCallback ), null, -1, false ); } } this.dtcTransactionManagerLock = new ReaderWriterLock(); this.nodeNameField = nodeName; // The DTC proxy doesn't like an empty string for node name on 64-bit platforms when // running as WOW64. It treats any non-null node name as a "remote" node and turns off // the WOW64 bit, causing problems when reading the registry. So if we got on empty // string for the node name, just treat it as null. if (( null != this.nodeNameField ) && ( 0 == this.nodeNameField.Length )) { this.nodeNameField = null; } if ( DiagnosticTrace.Verbose ) { DistributedTransactionManagerCreatedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), this.GetType(), this.nodeNameField ); } // Initialize the properties from config. configuredTransactionOptions.IsolationLevel = isolationLevelProperty = TransactionManager.DefaultIsolationLevel; configuredTransactionOptions.Timeout = timeoutProperty = TransactionManager.DefaultTimeout; this.internalResourceManager = new OletxInternalResourceManager( this ); dtcTransactionManagerLock.AcquireWriterLock( -1 ); try { this.dtcTransactionManager = new DtcTransactionManager( this.nodeNameField, this ); } finally { dtcTransactionManagerLock.ReleaseWriterLock(); } if (resourceManagerHashTable == null) { resourceManagerHashTable = new Hashtable(2); resourceManagerHashTableLock = new System.Threading.ReaderWriterLock(); } } internal OletxCommittableTransaction CreateTransaction( TransactionOptions properties ) { OletxCommittableTransaction tx = null; RealOletxTransaction realTransaction = null; ITransactionShim transactionShim = null; Guid txIdentifier = Guid.Empty; OutcomeEnlistment outcomeEnlistment = null; // Demand the distributed transation permission to create one of // these. DistributedTransactionPermission txPerm = new DistributedTransactionPermission( PermissionState.Unrestricted ); txPerm.Demand(); TransactionManager.ValidateIsolationLevel( properties.IsolationLevel ); // Never create a transaction with an IsolationLevel of Unspecified. if ( IsolationLevel.Unspecified == properties.IsolationLevel ) { properties.IsolationLevel = configuredTransactionOptions.IsolationLevel; } properties.Timeout = TransactionManager.ValidateTimeout( properties.Timeout ); this.dtcTransactionManagerLock.AcquireReaderLock( -1 ); try { // OletxTransactionIsolationLevel oletxIsoLevel = OletxTransactionManager.ConvertIsolationLevel( properties.IsolationLevel ); UInt32 oletxTimeout = DtcTransactionManager.AdjustTimeout( properties.Timeout ); outcomeEnlistment = new OutcomeEnlistment(); IntPtr outcomeEnlistmentHandle = IntPtr.Zero; RuntimeHelpers.PrepareConstrainedRegions(); try { outcomeEnlistmentHandle = HandleTable.AllocHandle( outcomeEnlistment ); dtcTransactionManager.ProxyShimFactory.BeginTransaction( oletxTimeout, oletxIsoLevel, outcomeEnlistmentHandle, out txIdentifier, out transactionShim ); } catch ( COMException ex ) { OletxTransactionManager.ProxyException( ex ); throw; } finally { if ( transactionShim == null && outcomeEnlistmentHandle != IntPtr.Zero ) { HandleTable.FreeHandle( outcomeEnlistmentHandle ); } } realTransaction = new RealOletxTransaction( this, transactionShim, outcomeEnlistment, txIdentifier, oletxIsoLevel, true ); tx = new OletxCommittableTransaction( realTransaction ); if ( DiagnosticTrace.Information ) { TransactionCreatedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), tx.TransactionTraceId ); } } finally { this.dtcTransactionManagerLock.ReleaseReaderLock(); } return tx; } internal OletxEnlistment ReenlistTransaction( Guid resourceManagerIdentifier, byte[] recoveryInformation, IEnlistmentNotificationInternal enlistmentNotification ) { if ( null == recoveryInformation ) { throw new ArgumentNullException( "recoveryInformation" ); } if ( null == enlistmentNotification ) { throw new ArgumentNullException( "enlistmentNotification" ); } // Now go find the resource manager in the collection. OletxResourceManager oletxResourceManager = RegisterResourceManager( resourceManagerIdentifier ); if ( null == oletxResourceManager ) { throw new ArgumentException( SR.GetString( SR.InvalidArgument ), "resourceManagerIdentifier" ); } if( oletxResourceManager.RecoveryCompleteCalledByApplication ) { throw new InvalidOperationException( SR.GetString( SR.ReenlistAfterRecoveryComplete )); } // Now ask the resource manager to reenlist. OletxEnlistment returnValue = oletxResourceManager.Reenlist( recoveryInformation.Length, recoveryInformation, enlistmentNotification ); return returnValue; } internal void ResourceManagerRecoveryComplete( Guid resourceManagerIdentifier ) { OletxResourceManager oletxRm = RegisterResourceManager( resourceManagerIdentifier ); if( oletxRm.RecoveryCompleteCalledByApplication ) { throw new InvalidOperationException( SR.GetString( SR.DuplicateRecoveryComplete )); } oletxRm.RecoveryComplete(); } internal OletxResourceManager RegisterResourceManager( Guid resourceManagerIdentifier ) { OletxResourceManager oletxResourceManager = null; resourceManagerHashTableLock.AcquireWriterLock(-1); try { // If this resource manager has already been registered, don't register it again. oletxResourceManager = resourceManagerHashTable[ resourceManagerIdentifier ] as OletxResourceManager; if ( null != oletxResourceManager ) { return oletxResourceManager; } oletxResourceManager = new OletxResourceManager( this, resourceManagerIdentifier ); resourceManagerHashTable.Add( resourceManagerIdentifier, oletxResourceManager ); } finally { resourceManagerHashTableLock.ReleaseWriterLock(); } return oletxResourceManager; } internal string CreationNodeName { get { return nodeNameField; } } internal OletxResourceManager FindOrRegisterResourceManager( Guid resourceManagerIdentifier ) { if( resourceManagerIdentifier == Guid.Empty ) { throw new ArgumentException( SR.GetString( SR.BadResourceManagerId ), "resourceManagerIdentifier" ); } OletxResourceManager oletxResourceManager = null; resourceManagerHashTableLock.AcquireReaderLock(-1); try { oletxResourceManager = resourceManagerHashTable[ resourceManagerIdentifier ] as OletxResourceManager; } finally { resourceManagerHashTableLock.ReleaseReaderLock(); } if ( null == oletxResourceManager ) { return RegisterResourceManager( resourceManagerIdentifier); } return oletxResourceManager; } internal DtcTransactionManager DtcTransactionManager { get { if ( ( this.dtcTransactionManagerLock.IsReaderLockHeld ) || ( this.dtcTransactionManagerLock.IsWriterLockHeld ) ) { if ( null == this.dtcTransactionManager ) { throw TransactionException.Create( SR.GetString( SR.TraceSourceOletx ), SR.GetString( SR.DtcTransactionManagerUnavailable ), null ); } return this.dtcTransactionManager; } else { // Internal programming error. A reader or writer lock should be held when this property is invoked. throw TransactionException.Create ( SR.GetString( SR.TraceSourceOletx ), SR.GetString( SR.InternalError ), null ); } } } internal string NodeName { get { return this.nodeNameField; } } internal static void ProxyException( COMException comException ) { if ( ( NativeMethods.XACT_E_CONNECTION_DOWN == comException.ErrorCode ) || ( NativeMethods.XACT_E_TMNOTAVAILABLE == comException.ErrorCode ) ) { throw TransactionManagerCommunicationException.Create( SR.GetString( SR.TraceSourceOletx ), SR.GetString( SR.TransactionManagerCommunicationException ), comException ); } if (( NativeMethods.XACT_E_NETWORK_TX_DISABLED == comException.ErrorCode )) { throw TransactionManagerCommunicationException.Create( SR.GetString( SR.TraceSourceOletx ), SR.GetString( SR.NetworkTransactionsDisabled ), comException ); } // Else if the error is a transaction oriented error, throw a TransactionException else if ( ( NativeMethods.XACT_E_FIRST <= comException.ErrorCode ) && ( NativeMethods.XACT_E_LAST >= comException.ErrorCode ) ) { // Special casing XACT_E_NOTRANSACTION if ( NativeMethods.XACT_E_NOTRANSACTION == comException.ErrorCode ) { throw TransactionException.Create( SR.GetString( SR.TraceSourceOletx ), SR.GetString( SR.TransactionAlreadyOver ), comException ); } throw TransactionException.Create( SR.GetString( SR.TraceSourceOletx ), comException.Message, comException ); } } internal void ReinitializeProxy() { // This is created by the static constructor. dtcTransactionManagerLock.AcquireWriterLock( -1 ); try { if ( null != dtcTransactionManager ) { dtcTransactionManager.ReleaseProxy(); } } finally { dtcTransactionManagerLock.ReleaseWriterLock(); } } internal static OletxTransactionIsolationLevel ConvertIsolationLevel( IsolationLevel isolationLevel ) { OletxTransactionIsolationLevel retVal; switch (isolationLevel) { case IsolationLevel.Serializable : retVal = OletxTransactionIsolationLevel.ISOLATIONLEVEL_SERIALIZABLE; break; case IsolationLevel.RepeatableRead : retVal = OletxTransactionIsolationLevel.ISOLATIONLEVEL_REPEATABLEREAD; break; case IsolationLevel.ReadCommitted : retVal = OletxTransactionIsolationLevel.ISOLATIONLEVEL_READCOMMITTED; break; case IsolationLevel.ReadUncommitted : retVal = OletxTransactionIsolationLevel.ISOLATIONLEVEL_READUNCOMMITTED; break; case IsolationLevel.Chaos: retVal = OletxTransactionIsolationLevel.ISOLATIONLEVEL_CHAOS; break; case IsolationLevel.Unspecified : retVal = OletxTransactionIsolationLevel.ISOLATIONLEVEL_UNSPECIFIED; break; default : retVal = OletxTransactionIsolationLevel.ISOLATIONLEVEL_SERIALIZABLE; break; } return retVal; } internal static IsolationLevel ConvertIsolationLevelFromProxyValue( OletxTransactionIsolationLevel proxyIsolationLevel ) { IsolationLevel retVal; switch (proxyIsolationLevel) { case OletxTransactionIsolationLevel.ISOLATIONLEVEL_SERIALIZABLE : retVal = IsolationLevel.Serializable; break; case OletxTransactionIsolationLevel.ISOLATIONLEVEL_REPEATABLEREAD : retVal = IsolationLevel.RepeatableRead; break; case OletxTransactionIsolationLevel.ISOLATIONLEVEL_READCOMMITTED : retVal = IsolationLevel.ReadCommitted; break; case OletxTransactionIsolationLevel.ISOLATIONLEVEL_READUNCOMMITTED : retVal = IsolationLevel.ReadUncommitted; break; case OletxTransactionIsolationLevel.ISOLATIONLEVEL_UNSPECIFIED : retVal = IsolationLevel.Unspecified; break; case OletxTransactionIsolationLevel.ISOLATIONLEVEL_CHAOS : retVal = IsolationLevel.Chaos; break; default : retVal = IsolationLevel.Serializable; break; } return retVal; } // Helper object for static synchronization internal static object ClassSyncObject { get { if( classSyncObject == null ) { object o = new object(); Interlocked.CompareExchange( ref classSyncObject, o, null ); } return classSyncObject; } } } internal class OletxInternalResourceManager { OletxTransactionManager oletxTm; Guid myGuid; internal IResourceManagerShim resourceManagerShim = null; internal OletxInternalResourceManager( OletxTransactionManager oletxTm ) { this.oletxTm = oletxTm; this.myGuid = Guid.NewGuid(); } public void TMDown() { // Let's set ourselves up for reinitialization with the proxy by releasing our // reference to the resource manager shim, which will release its reference // to the proxy when it destructs. this.resourceManagerShim = null; // We need to look through all the transactions and tell them about // the TMDown so they can tell their Phase0VolatileEnlistmentContainers. Transaction tx = null; RealOletxTransaction realTx = null; IDictionaryEnumerator tableEnum = null; if ( DiagnosticTrace.Verbose ) { MethodEnteredTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), "OletxInternalResourceManager.TMDown" ); } // make a local copy of the hash table to avoid possible deadlocks when we lock both the global hash table // and the transaction object. Hashtable txHashTable = null; lock( TransactionManager.PromotedTransactionTable.SyncRoot ) { txHashTable = (Hashtable) TransactionManager.PromotedTransactionTable.Clone(); } // No need to lock my hashtable, nobody is going to change it. tableEnum = txHashTable.GetEnumerator(); while ( tableEnum.MoveNext() ) { WeakReference txWeakRef = (WeakReference) tableEnum.Value; if ( null != txWeakRef ) { tx = (Transaction)txWeakRef.Target; if ( null != tx ) { realTx = tx.internalTransaction.PromotedTransaction.realOletxTransaction; // Only deal with transactions owned by my OletxTm. if ( realTx.OletxTransactionManagerInstance == this.oletxTm ) { realTx.TMDown(); } } } } // Now make a local copy of the hash table of resource managers and tell each of them. This is to // deal with Durable EDPR=true (phase0) enlistments. Each RM will also get a TMDown, but it will // come AFTER the "buggy" Phase0Request with abortHint=true - COMPlus bug 36760/36758. Hashtable rmHashTable = null; if ( null != OletxTransactionManager.resourceManagerHashTable ) { OletxTransactionManager.resourceManagerHashTableLock.AcquireReaderLock( Timeout.Infinite ); try { rmHashTable = (Hashtable) OletxTransactionManager.resourceManagerHashTable.Clone(); } finally { OletxTransactionManager.resourceManagerHashTableLock.ReleaseReaderLock(); } } if ( null != rmHashTable ) { // No need to lock my hashtable, nobody is going to change it. tableEnum = rmHashTable.GetEnumerator(); while ( tableEnum.MoveNext() ) { OletxResourceManager oletxRM = (OletxResourceManager) tableEnum.Value; if ( null != oletxRM ) { // When the RM spins through its enlistments, it will need to make sure that // the enlistment is for this particular TM. oletxRM.TMDownFromInternalRM( this.oletxTm ); } } } // Now let's reinitialize the shim. this.oletxTm.dtcTransactionManagerLock.AcquireWriterLock( -1 ); try { this.oletxTm.ReinitializeProxy(); } finally { this.oletxTm.dtcTransactionManagerLock.ReleaseWriterLock(); } if ( DiagnosticTrace.Verbose ) { MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), "OletxInternalResourceManager.TMDown" ); } } internal Guid Identifier { get { return this.myGuid; } } internal void CallReenlistComplete() { this.resourceManagerShim.ReenlistComplete(); } } } // 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
- SqlCacheDependencyDatabaseCollection.cs
- RepeaterDataBoundAdapter.cs
- ListViewInsertionMark.cs
- NavigatorOutput.cs
- CodePrimitiveExpression.cs
- RotateTransform3D.cs
- control.ime.cs
- PassportAuthenticationEventArgs.cs
- InputLanguageEventArgs.cs
- WebPartConnectionsConnectVerb.cs
- PriorityRange.cs
- EventBookmark.cs
- Package.cs
- URI.cs
- ElapsedEventArgs.cs
- Fx.cs
- DragDeltaEventArgs.cs
- OleDbParameter.cs
- RunWorkerCompletedEventArgs.cs
- ComponentCache.cs
- PathStreamGeometryContext.cs
- ImageInfo.cs
- ServiceSecurityContext.cs
- _HelperAsyncResults.cs
- CommunicationException.cs
- SoapHeader.cs
- SymbolEqualComparer.cs
- PriorityQueue.cs
- XmlSchemas.cs
- ITextView.cs
- ResourceProperty.cs
- AppSettingsReader.cs
- Triplet.cs
- CodeAttachEventStatement.cs
- TextElementCollectionHelper.cs
- _BasicClient.cs
- DataBindingCollectionEditor.cs
- ColumnMapCopier.cs
- LongValidator.cs
- HtmlFormParameterWriter.cs
- ComNativeDescriptor.cs
- ClientUIRequest.cs
- HitTestResult.cs
- VisualStyleInformation.cs
- OutputCacheModule.cs
- AuthenticationConfig.cs
- DataGridPreparingCellForEditEventArgs.cs
- SQLCharsStorage.cs
- IdentityHolder.cs
- EventProxy.cs
- GridViewRowEventArgs.cs
- ScrollChrome.cs
- OraclePermission.cs
- IPipelineRuntime.cs
- CursorConverter.cs
- StrictAndMessageFilter.cs
- RequestCacheValidator.cs
- SessionState.cs
- LocalFileSettingsProvider.cs
- DefaultTextStore.cs
- XsltCompileContext.cs
- ChtmlPageAdapter.cs
- bindurihelper.cs
- DropDownList.cs
- PropertyCondition.cs
- exports.cs
- Variable.cs
- PasswordDeriveBytes.cs
- Separator.cs
- IdentityHolder.cs
- XmlDocumentType.cs
- Propagator.JoinPropagator.cs
- DetailsViewModeEventArgs.cs
- MatcherBuilder.cs
- ExpressionPrefixAttribute.cs
- SizeKeyFrameCollection.cs
- RubberbandSelector.cs
- PeerToPeerException.cs
- PackageRelationship.cs
- TextBox.cs
- MultipleViewPattern.cs
- PointValueSerializer.cs
- Roles.cs
- ImageDrawing.cs
- CustomGrammar.cs
- XsdDateTime.cs
- DiagnosticsElement.cs
- oledbmetadatacollectionnames.cs
- VisualStyleTypesAndProperties.cs
- APCustomTypeDescriptor.cs
- XmlAttributeOverrides.cs
- HandledEventArgs.cs
- HtmlEncodedRawTextWriter.cs
- OleDragDropHandler.cs
- CreateParams.cs
- SynchronizingStream.cs
- ResourceDisplayNameAttribute.cs
- CfgSemanticTag.cs
- ContentElement.cs
- DataGridCellsPresenter.cs