|
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;
// Double-checked locking pattern requires volatile for read/write synchronization
internal static volatile 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();
}
}
}
|