You've already forked linux-packaging-mono
							
							
		
			
				
	
	
		
			1848 lines
		
	
	
		
			86 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			1848 lines
		
	
	
		
			86 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| //------------------------------------------------------------------------------
 | |
| // <copyright file="DbConnectionPool.cs" company="Microsoft">
 | |
| //      Copyright (c) Microsoft Corporation.  All rights reserved.
 | |
| // </copyright>
 | |
| // <owner current="true" primary="true">Microsoft</owner>
 | |
| // <owner current="true" primary="false">Microsoft</owner>
 | |
| //------------------------------------------------------------------------------
 | |
| 
 | |
| namespace System.Data.ProviderBase {
 | |
| 
 | |
|     using System;
 | |
|     using System.Collections;
 | |
|     using System.Collections.Generic;
 | |
|     using System.Data.Common;
 | |
|     using System.Data.SqlClient;
 | |
|     using System.Diagnostics;
 | |
|     using System.Globalization;
 | |
|     using System.Runtime.CompilerServices;
 | |
|     using System.Runtime.ConstrainedExecution;
 | |
|     using System.Runtime.InteropServices;
 | |
|     using System.Security;
 | |
|     using System.Security.Permissions;
 | |
|     using System.Security.Principal;
 | |
|     using System.Threading;
 | |
|     using System.Threading.Tasks;
 | |
|     using SysTx = System.Transactions;
 | |
|     using System.Runtime.Versioning;
 | |
|     using System.Diagnostics.CodeAnalysis;
 | |
|     using System.Collections.Concurrent;
 | |
| 
 | |
|     sealed internal class DbConnectionPool {
 | |
|         private enum State {
 | |
|             Initializing,
 | |
|             Running,
 | |
|             ShuttingDown,
 | |
|         }
 | |
| 
 | |
|         internal const Bid.ApiGroup PoolerTracePoints = Bid.ApiGroup.Pooling;
 | |
| 
 | |
|         // This class is a way to stash our cloned Tx key for later disposal when it's no longer needed.
 | |
|         // We can't get at the key in the dictionary without enumerating entries, so we stash an extra
 | |
|         // copy as part of the value.
 | |
|         sealed private class TransactedConnectionList : List<DbConnectionInternal> {
 | |
|             private SysTx.Transaction _transaction;
 | |
|             internal TransactedConnectionList(int initialAllocation, SysTx.Transaction tx) : base(initialAllocation) {
 | |
|                 _transaction = tx;
 | |
|             }
 | |
| 
 | |
|             internal void Dispose() {
 | |
|                 if (null != _transaction) {
 | |
|                     _transaction.Dispose();
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         sealed class PendingGetConnection {
 | |
|             public PendingGetConnection(long dueTime, DbConnection owner, TaskCompletionSource<DbConnectionInternal> completion, DbConnectionOptions userOptions) {
 | |
|                 DueTime = dueTime;
 | |
|                 Owner = owner;
 | |
|                 Completion = completion;
 | |
|             }
 | |
|             public long DueTime { get; private set; }
 | |
|             public DbConnection Owner { get; private set; }
 | |
|             public TaskCompletionSource<DbConnectionInternal> Completion { get; private set; }
 | |
|             public DbConnectionOptions UserOptions { get; private set; }
 | |
|         }
 | |
| 
 | |
|         sealed private class TransactedConnectionPool 
 | |
|         {
 | |
| 
 | |
|             Dictionary<SysTx.Transaction, TransactedConnectionList> _transactedCxns;
 | |
| 
 | |
|             DbConnectionPool _pool;
 | |
| 
 | |
|             private static int _objectTypeCount; // Bid counter
 | |
|             internal readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount);
 | |
| 
 | |
|             internal TransactedConnectionPool(DbConnectionPool pool)
 | |
|             {
 | |
|                 Debug.Assert(null != pool, "null pool?");
 | |
| 
 | |
|                 _pool = pool;
 | |
|                 _transactedCxns = new Dictionary<SysTx.Transaction, TransactedConnectionList> ();
 | |
|                 
 | |
|                 Bid.PoolerTrace("<prov.DbConnectionPool.TransactedConnectionPool.TransactedConnectionPool|RES|CPOOL> %d#, Constructed for connection pool %d#\n", ObjectID, _pool.ObjectID);
 | |
|             }
 | |
| 
 | |
|             internal int ObjectID {
 | |
|                 get {
 | |
|                     return _objectID;
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             internal DbConnectionPool Pool {
 | |
|                 get {
 | |
|                     return _pool;
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             internal DbConnectionInternal GetTransactedObject(SysTx.Transaction transaction) 
 | |
|             {
 | |
|                 Debug.Assert(null != transaction, "null transaction?");
 | |
| 
 | |
|                 DbConnectionInternal transactedObject = null;
 | |
| 
 | |
|                 TransactedConnectionList connections;
 | |
|                 bool txnFound = false;
 | |
|                 
 | |
|                 lock (_transactedCxns)
 | |
|                 {
 | |
|                     txnFound = _transactedCxns.TryGetValue ( transaction, out connections );
 | |
|                 }
 | |
| 
 | |
|                 // NOTE: GetTransactedObject is only used when AutoEnlist = True and the ambient transaction 
 | |
|                 //   (Sys.Txns.Txn.Current) is still valid/non-null. This, in turn, means that we don't need 
 | |
|                 //   to worry about a pending asynchronous TransactionCompletedEvent to trigger processing in
 | |
|                 //   TransactionEnded below and potentially wipe out the connections list underneath us. It
 | |
|                 //   is similarly alright if a pending addition to the connections list in PutTransactedObject
 | |
|                 //   below is not completed prior to the lock on the connections object here...getting a new
 | |
|                 //   connection is probably better than unnecessarily locking
 | |
|                 if (txnFound)
 | |
|                 {
 | |
|                     Debug.Assert ( connections != null );
 | |
| 
 | |
|                     // synchronize multi-threaded access with PutTransactedObject (TransactionEnded should
 | |
|                     //   not be a concern, see comments above)
 | |
|                     lock ( connections ) 
 | |
|                     {
 | |
|                         int i = connections.Count - 1;
 | |
|                         if (0 <= i)
 | |
|                         {
 | |
|                             transactedObject = connections[i];
 | |
|                             connections.RemoveAt(i);
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
| 
 | |
|                 if (null != transactedObject) {
 | |
|                     Bid.PoolerTrace("<prov.DbConnectionPool.TransactedConnectionPool.GetTransactedObject|RES|CPOOL> %d#, Transaction %d#, Connection %d#, Popped.\n", ObjectID, transaction.GetHashCode(), transactedObject.ObjectID);
 | |
|                 }
 | |
|                 return transactedObject;
 | |
|             }
 | |
| 
 | |
|             internal void PutTransactedObject(SysTx.Transaction transaction, DbConnectionInternal transactedObject) {
 | |
|                 Debug.Assert(null != transaction, "null transaction?");
 | |
|                 Debug.Assert(null != transactedObject, "null transactedObject?");
 | |
| 
 | |
|                 TransactedConnectionList connections;
 | |
|                 bool txnFound = false;
 | |
|                 
 | |
|                 // NOTE: because TransactionEnded is an asynchronous notification, there's no guarantee
 | |
|                 //   around the order in which PutTransactionObject and TransactionEnded are called. 
 | |
| 
 | |
|                 lock ( _transactedCxns )
 | |
|                 {
 | |
|                     // Check if a transacted pool has been created for this transaction
 | |
|                     if ( txnFound = _transactedCxns.TryGetValue ( transaction, out connections ) ) 
 | |
|                     {
 | |
|                         Debug.Assert ( connections != null );
 | |
| 
 | |
|                         // synchronize multi-threaded access with GetTransactedObject
 | |
|                         lock ( connections ) 
 | |
|                         {
 | |
|                             Debug.Assert(0 > connections.IndexOf(transactedObject), "adding to pool a second time?");
 | |
|                             Bid.PoolerTrace("<prov.DbConnectionPool.TransactedConnectionPool.PutTransactedObject|RES|CPOOL> %d#, Transaction %d#, Connection %d#, Pushing.\n", ObjectID, transaction.GetHashCode(), transactedObject.ObjectID);
 | |
| 
 | |
|                             connections.Add(transactedObject);
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
| 
 | |
|                 // 
 | |
| 
 | |
|                 if ( !txnFound )
 | |
|                 {
 | |
|                     // create the transacted pool, making sure to clone the associated transaction
 | |
|                     //   for use as a key in our internal dictionary of transactions and connections
 | |
|                     SysTx.Transaction transactionClone = null;
 | |
|                     TransactedConnectionList newConnections = null;
 | |
|                     
 | |
|                     try 
 | |
|                     {
 | |
|                         transactionClone = transaction.Clone();
 | |
|                         newConnections = new TransactedConnectionList(2, transactionClone); // start with only two connections in the list; most times we won't need that many.
 | |
| 
 | |
|                         lock ( _transactedCxns )
 | |
|                         {
 | |
|                             // NOTE: in the interim between the locks on the transacted pool (this) during 
 | |
|                             //   execution of this method, another thread (threadB) may have attempted to 
 | |
|                             //   add a different connection to the transacted pool under the same 
 | |
|                             //   transaction. As a result, threadB may have completed creating the
 | |
|                             //   transacted pool while threadA was processing the above instructions.
 | |
|                             if (txnFound = _transactedCxns.TryGetValue(transaction, out connections))
 | |
|                             {
 | |
|                                 Debug.Assert ( connections != null );
 | |
|                                 
 | |
|                                 // synchronize multi-threaded access with GetTransactedObject
 | |
|                                 lock ( connections ) 
 | |
|                                 {
 | |
|                                     Debug.Assert(0 > connections.IndexOf(transactedObject), "adding to pool a second time?");
 | |
|                                     Bid.PoolerTrace("<prov.DbConnectionPool.TransactedConnectionPool.PutTransactedObject|RES|CPOOL> %d#, Transaction %d#, Connection %d#, Pushing.\n", ObjectID, transaction.GetHashCode(), transactedObject.ObjectID);
 | |
| 
 | |
|                                     connections.Add(transactedObject);
 | |
|                                 }
 | |
|                             }
 | |
|                             else
 | |
|                             {
 | |
|                                 Bid.PoolerTrace("<prov.DbConnectionPool.TransactedConnectionPool.PutTransactedObject|RES|CPOOL> %d#, Transaction %d#, Connection %d#, Adding List to transacted pool.\n", ObjectID, transaction.GetHashCode(), transactedObject.ObjectID);
 | |
| 
 | |
|                                 // add the connection/transacted object to the list
 | |
|                                 newConnections.Add ( transactedObject );
 | |
|                                 
 | |
|                                 _transactedCxns.Add(transactionClone, newConnections);
 | |
|                                 transactionClone = null; // we've used it -- don't throw it or the TransactedConnectionList that references it away.                                
 | |
|                             }
 | |
|                         }
 | |
|                     }
 | |
|                     finally 
 | |
|                     {
 | |
|                         if (null != transactionClone)
 | |
|                         {
 | |
|                             if ( newConnections != null )
 | |
|                             {
 | |
|                                 // another thread created the transaction pool and thus the new 
 | |
|                                 //   TransactedConnectionList was not used, so dispose of it and
 | |
|                                 //   the transaction clone that it incorporates.
 | |
|                                 newConnections.Dispose();
 | |
|                             }
 | |
|                             else
 | |
|                             {
 | |
|                                 // memory allocation for newConnections failed...clean up unused transactionClone
 | |
|                                 transactionClone.Dispose();
 | |
|                             }
 | |
|                         }
 | |
|                     }
 | |
|                     Bid.PoolerTrace("<prov.DbConnectionPool.TransactedConnectionPool.PutTransactedObject|RES|CPOOL> %d#, Transaction %d#, Connection %d#, Added.\n", ObjectID, transaction.GetHashCode(), transactedObject.ObjectID );
 | |
|                 }
 | |
| 
 | |
| #if !MOBILE
 | |
|                 Pool.PerformanceCounters.NumberOfFreeConnections.Increment();
 | |
| #endif
 | |
|             }
 | |
| 
 | |
|             internal void TransactionEnded(SysTx.Transaction transaction, DbConnectionInternal transactedObject) 
 | |
|             {
 | |
|                 Bid.PoolerTrace("<prov.DbConnectionPool.TransactedConnectionPool.TransactionEnded|RES|CPOOL> %d#, Transaction %d#, Connection %d#, Transaction Completed\n", ObjectID, transaction.GetHashCode(), transactedObject.ObjectID);
 | |
| 
 | |
|                 TransactedConnectionList connections;
 | |
|                 int entry = -1;
 | |
|                 
 | |
|                 // NOTE: because TransactionEnded is an asynchronous notification, there's no guarantee
 | |
|                 //   around the order in which PutTransactionObject and TransactionEnded are called. As
 | |
|                 //   such, it is possible that the transaction does not yet have a pool created.
 | |
| 
 | |
|                 // 
 | |
| 
 | |
| 
 | |
| 
 | |
|                 lock ( _transactedCxns )
 | |
|                 {
 | |
|                     if (_transactedCxns.TryGetValue(transaction, out connections)) 
 | |
|                     {
 | |
|                         Debug.Assert ( connections != null );
 | |
| 
 | |
|                         bool shouldDisposeConnections = false;
 | |
| 
 | |
|                         // Lock connections to avoid conflict with GetTransactionObject
 | |
|                         lock (connections) 
 | |
|                         {
 | |
|                             entry = connections.IndexOf(transactedObject);
 | |
| 
 | |
|                             if ( entry >= 0 ) 
 | |
|                             {
 | |
|                                 connections.RemoveAt(entry);
 | |
|                             }
 | |
| 
 | |
|                             // Once we've completed all the ended notifications, we can
 | |
|                             // safely remove the list from the transacted pool.
 | |
|                             if (0 >= connections.Count)
 | |
|                             {
 | |
|                                 Bid.PoolerTrace("<prov.DbConnectionPool.TransactedConnectionPool.TransactionEnded|RES|CPOOL> %d#, Transaction %d#, Removing List from transacted pool.\n", ObjectID, transaction.GetHashCode());
 | |
|                                 _transactedCxns.Remove(transaction);
 | |
| 
 | |
|                                 // we really need to dispose our connection list; it may have 
 | |
|                                 // native resources via the tx and GC may not happen soon enough.
 | |
|                                 shouldDisposeConnections = true;
 | |
|                             }
 | |
|                         }
 | |
| 
 | |
|                         if (shouldDisposeConnections) {
 | |
|                             connections.Dispose();
 | |
|                         }
 | |
|                     }
 | |
|                     else
 | |
|                     {
 | |
|                         //Debug.Assert ( false, "TransactionCompletedEvent fired before PutTransactedObject put the connection in the transacted pool." );
 | |
|                         Bid.PoolerTrace("<prov.DbConnectionPool.TransactedConnectionPool.TransactionEnded|RES|CPOOL> %d#, Transaction %d#, Connection %d#, Transacted pool not yet created prior to transaction completing. Connection may be leaked.\n", ObjectID, transaction.GetHashCode(), transactedObject.ObjectID );
 | |
|                     }
 | |
|                 }
 | |
|                 
 | |
|                 // If (and only if) we found the connection in the list of
 | |
|                 // connections, we'll put it back...
 | |
|                 if (0 <= entry)  
 | |
|                 {
 | |
| #if !MOBILE
 | |
|                     Pool.PerformanceCounters.NumberOfFreeConnections.Decrement();
 | |
| #endif
 | |
|                     Pool.PutObjectFromTransactedPool(transactedObject);
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|         }
 | |
|         
 | |
|         private sealed class PoolWaitHandles : DbBuffer {
 | |
| 
 | |
|             private readonly Semaphore _poolSemaphore;
 | |
|             private readonly ManualResetEvent _errorEvent;
 | |
| 
 | |
|             // Using a Mutex requires ThreadAffinity because SQL CLR can swap
 | |
|             // the underlying Win32 thread associated with a managed thread in preemptive mode.
 | |
|             // Using an AutoResetEvent does not have that complication.
 | |
|             private readonly Semaphore _creationSemaphore;
 | |
| 
 | |
|             private readonly SafeHandle _poolHandle;
 | |
|             private readonly SafeHandle _errorHandle;
 | |
|             private readonly SafeHandle _creationHandle;
 | |
| 
 | |
|             private readonly int _releaseFlags;
 | |
| 
 | |
|             [ResourceExposure(ResourceScope.None)] // SxS: this method does not create named objects
 | |
|             [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
 | |
|             internal PoolWaitHandles() : base(3*IntPtr.Size) {
 | |
|                 bool mustRelease1 = false, mustRelease2 = false, mustRelease3 = false;
 | |
|                 
 | |
|                 _poolSemaphore = new Semaphore(0, MAX_Q_SIZE);
 | |
|                 _errorEvent = new ManualResetEvent(false);
 | |
|                 _creationSemaphore = new Semaphore(1, 1);
 | |
| 
 | |
|                 RuntimeHelpers.PrepareConstrainedRegions();
 | |
|                 try {
 | |
|                     // because SafeWaitHandle doesn't have reliability contract
 | |
|                     _poolHandle     = _poolSemaphore.SafeWaitHandle; 
 | |
|                     _errorHandle    = _errorEvent.SafeWaitHandle;
 | |
|                     _creationHandle = _creationSemaphore.SafeWaitHandle;
 | |
| 
 | |
|                     _poolHandle.DangerousAddRef(ref mustRelease1);
 | |
|                     _errorHandle.DangerousAddRef(ref mustRelease2);
 | |
|                     _creationHandle.DangerousAddRef(ref mustRelease3);
 | |
| 
 | |
|                     Debug.Assert(0 == SEMAPHORE_HANDLE, "SEMAPHORE_HANDLE");
 | |
|                     Debug.Assert(1 == ERROR_HANDLE, "ERROR_HANDLE");
 | |
|                     Debug.Assert(2 == CREATION_HANDLE, "CREATION_HANDLE");
 | |
| 
 | |
|                     WriteIntPtr(SEMAPHORE_HANDLE*IntPtr.Size, _poolHandle.DangerousGetHandle());
 | |
|                     WriteIntPtr(ERROR_HANDLE*IntPtr.Size,     _errorHandle.DangerousGetHandle());
 | |
|                     WriteIntPtr(CREATION_HANDLE*IntPtr.Size,  _creationHandle.DangerousGetHandle());
 | |
|                 }
 | |
|                 finally {
 | |
|                     if (mustRelease1) {
 | |
|                         _releaseFlags |= 1;
 | |
|                     }
 | |
|                     if (mustRelease2) {
 | |
|                         _releaseFlags |= 2;
 | |
|                     }
 | |
|                     if (mustRelease3) {
 | |
|                         _releaseFlags |= 4;
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|             
 | |
|             internal SafeHandle CreationHandle {
 | |
|                 [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
 | |
|                 get { return _creationHandle; }
 | |
|             }
 | |
| 
 | |
|             internal Semaphore CreationSemaphore {
 | |
|                 get { return _creationSemaphore; }
 | |
|             }
 | |
| 
 | |
|             internal ManualResetEvent ErrorEvent {
 | |
|                 get { return _errorEvent; }
 | |
|             }
 | |
| 
 | |
|             internal Semaphore PoolSemaphore {
 | |
|                 get { return _poolSemaphore; }
 | |
|             }
 | |
| 
 | |
|             protected override bool ReleaseHandle() {
 | |
|                 // NOTE: The SafeHandle class guarantees this will be called exactly once.
 | |
|                 // we know we can touch these other managed objects because of our original DangerousAddRef
 | |
|                 if (0 != (1 & _releaseFlags)) {
 | |
|                     _poolHandle.DangerousRelease();
 | |
|                 }
 | |
|                 if (0 != (2 & _releaseFlags)) {
 | |
|                     _errorHandle.DangerousRelease();
 | |
|                 }
 | |
|                 if (0 != (4 & _releaseFlags)) {
 | |
|                     _creationHandle.DangerousRelease();
 | |
|                 }
 | |
|                 return base.ReleaseHandle();
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         private const int MAX_Q_SIZE    = (int)0x00100000;
 | |
| 
 | |
|         // The order of these is important; we want the WaitAny call to be signaled
 | |
|         // for a free object before a creation signal.  Only the index first signaled
 | |
|         // object is returned from the WaitAny call.
 | |
|         private const int SEMAPHORE_HANDLE = (int)0x0;
 | |
|         private const int ERROR_HANDLE     = (int)0x1;
 | |
|         private const int CREATION_HANDLE  = (int)0x2;
 | |
|         private const int BOGUS_HANDLE     = (int)0x3;
 | |
| 
 | |
|         private const int WAIT_OBJECT_0 = 0;
 | |
|         private const int WAIT_TIMEOUT   = (int)0x102;
 | |
|         private const int WAIT_ABANDONED = (int)0x80;
 | |
|         private const int WAIT_FAILED    = -1;
 | |
| 
 | |
|         private const int ERROR_WAIT_DEFAULT = 5 * 1000; // 5 seconds
 | |
| 
 | |
|         // we do want a testable, repeatable set of generated random numbers
 | |
|         private static readonly Random _random = new Random(5101977); // Value obtained from Dave Driver
 | |
| 
 | |
|         private readonly int              _cleanupWait;
 | |
|         private readonly DbConnectionPoolIdentity _identity;
 | |
| 
 | |
|         private readonly DbConnectionFactory          _connectionFactory;
 | |
|         private readonly DbConnectionPoolGroup        _connectionPoolGroup;
 | |
|         private readonly DbConnectionPoolGroupOptions _connectionPoolGroupOptions;
 | |
|         private          DbConnectionPoolProviderInfo _connectionPoolProviderInfo;
 | |
| 
 | |
|         /// <summary>
 | |
|         /// The private member which carries the set of authenticationcontexts for this pool (based on the user's identity).
 | |
|         /// </summary>
 | |
|         private readonly ConcurrentDictionary<DbConnectionPoolAuthenticationContextKey, DbConnectionPoolAuthenticationContext> _pooledDbAuthenticationContexts;
 | |
| 
 | |
|         private State                     _state;
 | |
| 
 | |
|         private readonly ConcurrentStack<DbConnectionInternal> _stackOld = new ConcurrentStack<DbConnectionInternal>();
 | |
|         private readonly ConcurrentStack<DbConnectionInternal> _stackNew = new ConcurrentStack<DbConnectionInternal>();
 | |
| 
 | |
|         private readonly ConcurrentQueue<PendingGetConnection> _pendingOpens = new ConcurrentQueue<PendingGetConnection>();
 | |
|         private int _pendingOpensWaiting = 0;
 | |
| 
 | |
|         private readonly WaitCallback     _poolCreateRequest;
 | |
| 
 | |
|         private int                       _waitCount;
 | |
|         private readonly PoolWaitHandles  _waitHandles;
 | |
| 
 | |
|         private Exception                 _resError;
 | |
|         private volatile bool             _errorOccurred;
 | |
| 
 | |
|         private int                       _errorWait;
 | |
|         private Timer                     _errorTimer;
 | |
| 
 | |
|         private Timer                     _cleanupTimer;
 | |
| 
 | |
|         private readonly TransactedConnectionPool _transactedConnectionPool;
 | |
| 
 | |
|         private readonly List<DbConnectionInternal> _objectList;
 | |
|         private int                       _totalObjects;
 | |
| 
 | |
|         private static int _objectTypeCount; // Bid counter
 | |
|         internal readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount);
 | |
| 
 | |
|         // only created by DbConnectionPoolGroup.GetConnectionPool
 | |
|         internal DbConnectionPool(
 | |
|                             DbConnectionFactory connectionFactory,
 | |
|                             DbConnectionPoolGroup connectionPoolGroup,
 | |
|                             DbConnectionPoolIdentity identity,
 | |
|                             DbConnectionPoolProviderInfo connectionPoolProviderInfo ) {
 | |
|             Debug.Assert(ADP.IsWindowsNT, "Attempting to construct a connection pool on Win9x?");
 | |
|             Debug.Assert(null != connectionPoolGroup, "null connectionPoolGroup");
 | |
| 
 | |
|             if ((null != identity) && identity.IsRestricted) {
 | |
|                 throw ADP.InternalError(ADP.InternalErrorCode.AttemptingToPoolOnRestrictedToken);
 | |
|             }
 | |
| 
 | |
|             _state= State.Initializing;
 | |
| 
 | |
|             lock(_random) { // Random.Next is not thread-safe
 | |
|                 _cleanupWait = _random.Next(12, 24)*10*1000; // 2-4 minutes in 10 sec intervals, WebData 103603
 | |
|             }
 | |
| 
 | |
|             _connectionFactory = connectionFactory;
 | |
|             _connectionPoolGroup = connectionPoolGroup;
 | |
|             _connectionPoolGroupOptions = connectionPoolGroup.PoolGroupOptions;
 | |
|             _connectionPoolProviderInfo = connectionPoolProviderInfo;
 | |
|             _identity = identity;
 | |
| 
 | |
|             _waitHandles = new PoolWaitHandles();
 | |
| 
 | |
|             _errorWait      = ERROR_WAIT_DEFAULT;
 | |
|             _errorTimer     = null;  // No error yet.
 | |
| 
 | |
|             _objectList     = new List<DbConnectionInternal>(MaxPoolSize);
 | |
| 
 | |
|             _pooledDbAuthenticationContexts = new ConcurrentDictionary<DbConnectionPoolAuthenticationContextKey, DbConnectionPoolAuthenticationContext>(concurrencyLevel: 4 * Environment.ProcessorCount /* default value in ConcurrentDictionary*/,
 | |
|                                                                                                                                                         capacity: 2);
 | |
| 
 | |
|             if(ADP.IsPlatformNT5) {
 | |
|                 _transactedConnectionPool = new TransactedConnectionPool(this);
 | |
|             }
 | |
| 
 | |
|             _poolCreateRequest = new WaitCallback(PoolCreateRequest); // used by CleanupCallback
 | |
|             _state = State.Running;
 | |
| 
 | |
|             Bid.PoolerTrace("<prov.DbConnectionPool.DbConnectionPool|RES|CPOOL> %d#, Constructed.\n", ObjectID);
 | |
| 
 | |
|             //_cleanupTimer & QueuePoolCreateRequest is delayed until DbConnectionPoolGroup calls
 | |
|             // StartBackgroundCallbacks after pool is actually in the collection
 | |
|         }
 | |
| 
 | |
|         private int CreationTimeout {
 | |
|             get { return PoolGroupOptions.CreationTimeout; }
 | |
|         }
 | |
| 
 | |
|         internal int Count {
 | |
|             get { return _totalObjects; }
 | |
|         }
 | |
| 
 | |
|         internal DbConnectionFactory ConnectionFactory {
 | |
|             get { return _connectionFactory; }
 | |
|         }
 | |
| 
 | |
|         internal bool ErrorOccurred {
 | |
|             get { return _errorOccurred; }
 | |
|         }
 | |
| 
 | |
|         private bool HasTransactionAffinity {
 | |
|             get { return PoolGroupOptions.HasTransactionAffinity; }
 | |
|         }
 | |
| 
 | |
|         internal TimeSpan LoadBalanceTimeout {
 | |
|             get { return PoolGroupOptions.LoadBalanceTimeout; }
 | |
|         }
 | |
| 
 | |
|         private bool NeedToReplenish {
 | |
|             get {
 | |
|                 if (State.Running != _state) // SQL BU DT 364595 - don't allow connection create when not running.
 | |
|                     return false;
 | |
| 
 | |
|                 int totalObjects = Count;
 | |
| 
 | |
|                 if (totalObjects >= MaxPoolSize)
 | |
|                     return false;
 | |
| 
 | |
|                 if (totalObjects < MinPoolSize)
 | |
|                     return true;
 | |
| 
 | |
|                 int freeObjects        = (_stackNew.Count + _stackOld.Count);
 | |
|                 int waitingRequests    = _waitCount;
 | |
|                 bool needToReplenish = (freeObjects < waitingRequests) || ((freeObjects == waitingRequests) && (totalObjects > 1));
 | |
| 
 | |
|                 return needToReplenish;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         internal DbConnectionPoolIdentity Identity {
 | |
|             get { return _identity; }
 | |
|         }
 | |
|         
 | |
|         internal bool IsRunning {
 | |
|             get { return State.Running == _state; }
 | |
|         }
 | |
| 
 | |
|         private int MaxPoolSize {
 | |
|             get { return PoolGroupOptions.MaxPoolSize; }
 | |
|         }
 | |
| 
 | |
|         private int MinPoolSize {
 | |
|             get { return PoolGroupOptions.MinPoolSize; }
 | |
|         }
 | |
| 
 | |
|         internal int ObjectID {
 | |
|             get {
 | |
|                 return _objectID;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         internal DbConnectionPoolCounters PerformanceCounters {
 | |
|             get { return _connectionFactory.PerformanceCounters; }
 | |
|         }
 | |
| 
 | |
|         internal DbConnectionPoolGroup PoolGroup {
 | |
|             get { return _connectionPoolGroup; }
 | |
|         }
 | |
| 
 | |
|         internal DbConnectionPoolGroupOptions PoolGroupOptions {
 | |
|             get { return _connectionPoolGroupOptions; }
 | |
|         }
 | |
| 
 | |
|         internal DbConnectionPoolProviderInfo ProviderInfo {
 | |
|             get { return _connectionPoolProviderInfo; }
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Return the pooled authentication contexts.
 | |
|         /// </summary>
 | |
|         internal ConcurrentDictionary<DbConnectionPoolAuthenticationContextKey, DbConnectionPoolAuthenticationContext> AuthenticationContexts
 | |
|         {
 | |
|             get
 | |
|             {
 | |
|                 return _pooledDbAuthenticationContexts;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         internal bool UseLoadBalancing {
 | |
|             get { return PoolGroupOptions.UseLoadBalancing; }
 | |
|         }
 | |
| 
 | |
|         private bool UsingIntegrateSecurity {
 | |
|             get { return (null != _identity && DbConnectionPoolIdentity.NoIdentity != _identity); }
 | |
|         }
 | |
| 
 | |
|         private void CleanupCallback(Object state) {
 | |
|             // Called when the cleanup-timer ticks over.
 | |
| 
 | |
|             // This is the automatic prunning method.  Every period, we will
 | |
|             // perform a two-step process:
 | |
|             //
 | |
|             // First, for each free object above MinPoolSize, we will obtain a
 | |
|             // semaphore representing one object and destroy one from old stack.
 | |
|             // We will continue this until we either reach MinPoolSize, we are
 | |
|             // unable to obtain a free object, or we have exhausted all the
 | |
|             // objects on the old stack.
 | |
|             //
 | |
|             // Second we move all free objects on the new stack to the old stack.
 | |
|             // So, every period the objects on the old stack are destroyed and
 | |
|             // the objects on the new stack are pushed to the old stack.  All
 | |
|             // objects that are currently out and in use are not on either stack.
 | |
|             //
 | |
|             // With this logic, objects are pruned from the pool if unused for
 | |
|             // at least one period but not more than two periods.
 | |
| 
 | |
|             Bid.PoolerTrace("<prov.DbConnectionPool.CleanupCallback|RES|INFO|CPOOL> %d#\n", ObjectID);
 | |
| 
 | |
|             // Destroy free objects that put us above MinPoolSize from old stack.
 | |
|             while(Count > MinPoolSize) { // While above MinPoolSize...
 | |
| 
 | |
|                 if (_waitHandles.PoolSemaphore.WaitOne(0, false) /* != WAIT_TIMEOUT */) {
 | |
|                     // We obtained a objects from the semaphore.
 | |
|                     DbConnectionInternal obj;
 | |
| 
 | |
|                     if (_stackOld.TryPop(out obj)) {
 | |
|                         Debug.Assert(obj != null, "null connection is not expected");
 | |
|                         // If we obtained one from the old stack, destroy it.
 | |
| #if !MOBILE
 | |
|                         PerformanceCounters.NumberOfFreeConnections.Decrement();
 | |
| #endif
 | |
| 
 | |
|                         // Transaction roots must survive even aging out (TxEnd event will clean them up).
 | |
|                         bool shouldDestroy = true;
 | |
|                         lock (obj) {    // Lock to prevent race condition window between IsTransactionRoot and shouldDestroy assignment
 | |
|                             if (obj.IsTransactionRoot) {
 | |
|                                 shouldDestroy = false;
 | |
|                             }
 | |
|                         }
 | |
| 
 | |
|                         // !!!!!!!!!! WARNING !!!!!!!!!!!!!
 | |
|                         //   ONLY touch obj after lock release if shouldDestroy is false!!!  Otherwise, it may be destroyed
 | |
|                         //   by transaction-end thread!
 | |
| 
 | |
|                         // Note that there is a minor race condition between this task and the transaction end event, if the latter runs 
 | |
|                         //  between the lock above and the SetInStasis call below. The reslult is that the stasis counter may be
 | |
|                         //  incremented without a corresponding decrement (the transaction end task is normally expected
 | |
|                         //  to decrement, but will only do so if the stasis flag is set when it runs). I've minimized the size
 | |
|                         //  of the window, but we aren't totally eliminating it due to SetInStasis needing to do bid tracing, which
 | |
|                         //  we don't want to do under this lock, if possible. It should be possible to eliminate this race condition with
 | |
|                         //  more substantial re-architecture of the pool, but we don't have the time to do that work for the current release.
 | |
| 
 | |
|                         if (shouldDestroy) {
 | |
|                             DestroyObject(obj);
 | |
|                         }
 | |
|                         else {
 | |
|                             obj.SetInStasis();
 | |
|                         }
 | |
|                     }
 | |
|                     else {
 | |
|                         // Else we exhausted the old stack (the object the
 | |
|                         // semaphore represents is on the new stack), so break.
 | |
|                         _waitHandles.PoolSemaphore.Release(1);
 | |
|                         break;
 | |
|                     }
 | |
|                 }
 | |
|                 else {
 | |
|                     break;
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             // Push to the old-stack.  For each free object, move object from
 | |
|             // new stack to old stack.
 | |
|             if(_waitHandles.PoolSemaphore.WaitOne(0, false) /* != WAIT_TIMEOUT */) {
 | |
|                 for(;;) {
 | |
|                     DbConnectionInternal obj;
 | |
| 
 | |
|                     if (!_stackNew.TryPop(out obj))
 | |
|                         break;
 | |
| 
 | |
|                     Debug.Assert(obj != null, "null connection is not expected");
 | |
| 
 | |
|                     Bid.PoolerTrace("<prov.DbConnectionPool.CleanupCallback|RES|INFO|CPOOL> %d#, ChangeStacks=%d#\n", ObjectID, obj.ObjectID);
 | |
| 
 | |
|                     Debug.Assert(!obj.IsEmancipated, "pooled object not in pool");
 | |
|                     Debug.Assert(obj.CanBePooled,     "pooled object is not poolable");
 | |
| 
 | |
|                     _stackOld.Push(obj);
 | |
|                 }
 | |
|                 _waitHandles.PoolSemaphore.Release(1);
 | |
|             }
 | |
| 
 | |
|             // Queue up a request to bring us up to MinPoolSize
 | |
|             QueuePoolCreateRequest();
 | |
|         }
 | |
| 
 | |
|         internal void Clear() {
 | |
|             Bid.PoolerTrace("<prov.DbConnectionPool.Clear|RES|CPOOL> %d#, Clearing.\n", ObjectID);
 | |
| 
 | |
|             DbConnectionInternal obj;
 | |
| 
 | |
|             // First, quickly doom everything.
 | |
|             lock(_objectList) {
 | |
|                 int count = _objectList.Count;
 | |
| 
 | |
|                 for (int i = 0; i < count; ++i) {
 | |
|                     obj = _objectList[i];
 | |
| 
 | |
|                     if (null != obj) {
 | |
|                         obj.DoNotPoolThisConnection();
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             // Second, dispose of all the free connections.
 | |
|             while (_stackNew.TryPop(out obj)) {
 | |
|                 Debug.Assert(obj != null, "null connection is not expected");
 | |
| #if !MOBILE
 | |
|                 PerformanceCounters.NumberOfFreeConnections.Decrement();
 | |
| #endif
 | |
|                 DestroyObject(obj);
 | |
|             }
 | |
|             while (_stackOld.TryPop(out obj)) {
 | |
|                 Debug.Assert(obj != null, "null connection is not expected");
 | |
| #if !MOBILE
 | |
|                 PerformanceCounters.NumberOfFreeConnections.Decrement();
 | |
| #endif
 | |
|                 DestroyObject(obj);
 | |
|             }
 | |
| 
 | |
|             // Finally, reclaim everything that's emancipated (which, because
 | |
|             // it's been doomed, will cause it to be disposed of as well)
 | |
|             ReclaimEmancipatedObjects();
 | |
| 
 | |
|             Bid.PoolerTrace("<prov.DbConnectionPool.Clear|RES|CPOOL> %d#, Cleared.\n", ObjectID);
 | |
|         }
 | |
| 
 | |
|         private Timer CreateCleanupTimer() {
 | |
|             return (new Timer(new TimerCallback(this.CleanupCallback), null, _cleanupWait, _cleanupWait));
 | |
|         }
 | |
| 
 | |
|         private bool IsBlockingPeriodEnabled()
 | |
|         {
 | |
|             var poolGroupConnectionOptions = _connectionPoolGroup.ConnectionOptions as SqlConnectionString;
 | |
|             if (poolGroupConnectionOptions == null)
 | |
|             {
 | |
|                 return true;
 | |
|             }
 | |
| 
 | |
|             var policy = poolGroupConnectionOptions.PoolBlockingPeriod;
 | |
| 
 | |
|             switch (policy)
 | |
|             {
 | |
|                 case PoolBlockingPeriod.Auto:
 | |
|                 {
 | |
|                     if (ADP.IsAzureSqlServerEndpoint(poolGroupConnectionOptions.DataSource))
 | |
|                     {
 | |
|                         return false; // in Azure it will be Disabled
 | |
|                     }
 | |
|                     else
 | |
|                     {
 | |
|                         return true; // in Non Azure, it will be Enabled
 | |
|                     }
 | |
|                 }
 | |
|                 case PoolBlockingPeriod.AlwaysBlock:
 | |
|                 {
 | |
|                     return true; //Enabled
 | |
|                 }
 | |
|                 case PoolBlockingPeriod.NeverBlock:
 | |
|                 {
 | |
|                     return false; //Disabled
 | |
|                 }
 | |
|                 default:
 | |
|                 {
 | |
|                     //we should never get into this path.
 | |
|                     Debug.Fail("Unknown PoolBlockingPeriod. Please specify explicit results in above switch case statement.");
 | |
|                     return true;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         private DbConnectionInternal CreateObject(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection) {
 | |
|             DbConnectionInternal newObj = null;
 | |
| 
 | |
|             try {
 | |
|                 newObj = _connectionFactory.CreatePooledConnection(this, owningObject, _connectionPoolGroup.ConnectionOptions, _connectionPoolGroup.PoolKey, userOptions);
 | |
|                 if (null == newObj) {
 | |
|                     throw ADP.InternalError(ADP.InternalErrorCode.CreateObjectReturnedNull);    // CreateObject succeeded, but null object
 | |
|                 }
 | |
|                 if (!newObj.CanBePooled) {
 | |
|                     throw ADP.InternalError(ADP.InternalErrorCode.NewObjectCannotBePooled);        // CreateObject succeeded, but non-poolable object
 | |
|                 }
 | |
|                 newObj.PrePush(null);
 | |
| 
 | |
|                 lock (_objectList) {
 | |
|                     if ((oldConnection != null) && (oldConnection.Pool == this)) {
 | |
|                         _objectList.Remove(oldConnection);
 | |
|                     }
 | |
|                     _objectList.Add(newObj);
 | |
|                     _totalObjects = _objectList.Count;
 | |
| #if !MOBILE
 | |
|                     PerformanceCounters.NumberOfPooledConnections.Increment();   // 
 | |
| #endif
 | |
|                 }
 | |
| 
 | |
|                 // If the old connection belonged to another pool, we need to remove it from that
 | |
|                 if (oldConnection != null) {
 | |
|                     var oldConnectionPool = oldConnection.Pool;
 | |
|                     if (oldConnectionPool != null && oldConnectionPool != this) {
 | |
|                         Debug.Assert(oldConnectionPool._state == State.ShuttingDown, "Old connections pool should be shutting down");
 | |
|                         lock (oldConnectionPool._objectList) {
 | |
|                             oldConnectionPool._objectList.Remove(oldConnection);
 | |
|                             oldConnectionPool._totalObjects = oldConnectionPool._objectList.Count;
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
|                 Bid.PoolerTrace("<prov.DbConnectionPool.CreateObject|RES|CPOOL> %d#, Connection %d#, Added to pool.\n", ObjectID, newObj.ObjectID);
 | |
| 
 | |
|                 // Reset the error wait:
 | |
|                 _errorWait = ERROR_WAIT_DEFAULT;
 | |
|             }
 | |
|             catch(Exception e)   {
 | |
|                 // 
 | |
|                 if (!ADP.IsCatchableExceptionType(e)) {
 | |
|                     throw;
 | |
|                 }
 | |
| 
 | |
|                 ADP.TraceExceptionForCapture(e);
 | |
| 
 | |
|                 if (!IsBlockingPeriodEnabled())
 | |
|                 {
 | |
|                     throw;
 | |
|                 }
 | |
| 
 | |
|                 newObj = null; // set to null, so we do not return bad new object
 | |
|                 // Failed to create instance
 | |
|                 _resError = e;
 | |
| 
 | |
|                 // VSTFDEVDIV 479561: Make sure the timer starts even if ThreadAbort occurs after setting the ErrorEvent.
 | |
| 
 | |
|                 // timer allocation has to be done out of CER block
 | |
|                 Timer t = new Timer(new TimerCallback(this.ErrorCallback), null, Timeout.Infinite, Timeout.Infinite);
 | |
|                 bool timerIsNotDisposed;
 | |
|                 RuntimeHelpers.PrepareConstrainedRegions();
 | |
|                 try{} finally {
 | |
|                     _waitHandles.ErrorEvent.Set();                    
 | |
|                     _errorOccurred = true;
 | |
|                     
 | |
|                     // Enable the timer.
 | |
|                     // Note that the timer is created to allow periodic invocation. If ThreadAbort occurs in the middle of ErrorCallback,
 | |
|                     // the timer will restart. Otherwise, the timer callback (ErrorCallback) destroys the timer after resetting the error to avoid second callback.
 | |
|                     _errorTimer = t;
 | |
|                     timerIsNotDisposed = t.Change(_errorWait, _errorWait);
 | |
|                 }
 | |
| 
 | |
|                 Debug.Assert(timerIsNotDisposed, "ErrorCallback timer has been disposed");
 | |
| 
 | |
|                 if (30000 < _errorWait) {
 | |
|                     _errorWait = 60000;
 | |
|                 }
 | |
|                 else {
 | |
|                     _errorWait *= 2;
 | |
|                 }
 | |
|                 throw;
 | |
|             }
 | |
|             return newObj;
 | |
|         }
 | |
| 
 | |
|         private void DeactivateObject(DbConnectionInternal obj) 
 | |
|         {
 | |
|             Bid.PoolerTrace("<prov.DbConnectionPool.DeactivateObject|RES|CPOOL> %d#, Connection %d#, Deactivating.\n", ObjectID, obj.ObjectID);
 | |
| 
 | |
|             obj.DeactivateConnection(); // we presume this operation is safe outside of a lock...
 | |
|             
 | |
|             bool returnToGeneralPool = false;
 | |
|             bool destroyObject = false;
 | |
|             bool rootTxn = false;
 | |
|             
 | |
|             if ( obj.IsConnectionDoomed ) 
 | |
|             {
 | |
|                 // the object is not fit for reuse -- just dispose of it.
 | |
|                 destroyObject = true;
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 // NOTE: constructor should ensure that current state cannot be State.Initializing, so it can only
 | |
|                 //   be State.Running or State.ShuttingDown
 | |
|                 Debug.Assert ( _state == State.Running || _state == State.ShuttingDown );
 | |
|                 
 | |
|                 lock (obj) 
 | |
|                 {
 | |
|                     // A connection with a delegated transaction cannot currently
 | |
|                     // be returned to a different customer until the transaction
 | |
|                     // actually completes, so we send it into Stasis -- the SysTx
 | |
|                     // transaction object will ensure that it is owned (not lost),
 | |
|                     // and it will be certain to put it back into the pool.                    
 | |
| 
 | |
|                     if ( _state == State.ShuttingDown )
 | |
|                     {
 | |
|                         if ( obj.IsTransactionRoot )
 | |
|                         {
 | |
|                             // SQLHotfix# 50003503 - connections that are affiliated with a 
 | |
|                             //   root transaction and that also happen to be in a connection 
 | |
|                             //   pool that is being shutdown need to be put in stasis so that 
 | |
|                             //   the root transaction isn't effectively orphaned with no 
 | |
|                             //   means to promote itself to a full delegated transaction or
 | |
|                             //   Commit or Rollback
 | |
|                             obj.SetInStasis();
 | |
|                             rootTxn = true;
 | |
|                         }
 | |
|                         else
 | |
|                         {
 | |
|                             // connection is being closed and the pool has been marked as shutting
 | |
|                             //   down, so destroy this object.
 | |
|                             destroyObject = true;
 | |
|                         }
 | |
|                     }
 | |
|                     else
 | |
|                     {
 | |
|                         if ( obj.IsNonPoolableTransactionRoot )
 | |
|                         {
 | |
|                             obj.SetInStasis();
 | |
|                             rootTxn = true;
 | |
|                         }
 | |
|                         else if ( obj.CanBePooled )
 | |
|                         {
 | |
|                             // We must put this connection into the transacted pool
 | |
|                             // while inside a lock to prevent a race condition with
 | |
|                             // the transaction asyncronously completing on a second
 | |
|                             // thread.
 | |
| 
 | |
|                             SysTx.Transaction transaction = obj.EnlistedTransaction;
 | |
|                             if (null != transaction) 
 | |
|                             {
 | |
|                                 // NOTE: we're not locking on _state, so it's possible that its
 | |
|                                 //   value could change between the conditional check and here.
 | |
|                                 //   Although perhaps not ideal, this is OK because the 
 | |
|                                 //   DelegatedTransactionEnded event will clean up the
 | |
|                                 //   connection appropriately regardless of the pool state.
 | |
|                                 Debug.Assert ( _transactedConnectionPool != null, "Transacted connection pool was not expected to be null.");
 | |
|                                 _transactedConnectionPool.PutTransactedObject(transaction, obj);
 | |
|                                 rootTxn = true;
 | |
|                             }
 | |
|                             else
 | |
|                             {
 | |
|                                 // return to general pool
 | |
|                                 returnToGeneralPool = true;
 | |
|                             }
 | |
|                         }
 | |
|                         else
 | |
|                         {
 | |
|                             if ( obj.IsTransactionRoot && !obj.IsConnectionDoomed )
 | |
|                             {
 | |
|                                 // SQLHotfix# 50003503 - if the object cannot be pooled but is a transaction
 | |
|                                 //   root, then we must have hit one of two race conditions:
 | |
|                                 //       1) PruneConnectionPoolGroups shutdown the pool and marked this connection 
 | |
|                                 //          as non-poolable while we were processing within this lock
 | |
|                                 //       2) The LoadBalancingTimeout expired on this connection and marked this
 | |
|                                 //          connection as DoNotPool.
 | |
|                                 //
 | |
|                                 //   This connection needs to be put in stasis so that the root transaction isn't
 | |
|                                 //   effectively orphaned with no means to promote itself to a full delegated 
 | |
|                                 //   transaction or Commit or Rollback
 | |
|                                 obj.SetInStasis();
 | |
|                                 rootTxn = true;
 | |
|                             }
 | |
|                             else
 | |
|                             {
 | |
|                                 // object is not fit for reuse -- just dispose of it
 | |
|                                 destroyObject = true;
 | |
|                             }
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|             
 | |
|             if (returnToGeneralPool) 
 | |
|             {
 | |
|                 // Only push the connection into the general pool if we didn't
 | |
|                 //   already push it onto the transacted pool, put it into stasis,
 | |
|                 //   or want to destroy it.
 | |
|                 Debug.Assert ( destroyObject == false );
 | |
|                 PutNewObject(obj);
 | |
|             }
 | |
|             else if ( destroyObject )
 | |
|             {
 | |
|                 // VSTFDEVDIV# 479556 - connections that have been marked as no longer 
 | |
|                 //   poolable (e.g. exceeded their connection lifetime) are not, in fact,
 | |
|                 //   returned to the general pool
 | |
|                 DestroyObject(obj);
 | |
|                 QueuePoolCreateRequest();
 | |
|             }
 | |
| 
 | |
|             //-------------------------------------------------------------------------------------
 | |
|             // postcondition
 | |
| 
 | |
|             // ensure that the connection was processed
 | |
|             Debug.Assert ( rootTxn == true || returnToGeneralPool == true || destroyObject == true );
 | |
| 
 | |
|             // 
 | |
|         }
 | |
| 
 | |
|         internal void DestroyObject(DbConnectionInternal obj) {
 | |
|             // A connection with a delegated transaction cannot be disposed of
 | |
|             // until the delegated transaction has actually completed.  Instead,
 | |
|             // we simply leave it alone; when the transaction completes, it will
 | |
|             // come back through PutObjectFromTransactedPool, which will call us
 | |
|             // again.
 | |
|             if (obj.IsTxRootWaitingForTxEnd) {
 | |
|                 Bid.PoolerTrace("<prov.DbConnectionPool.DestroyObject|RES|CPOOL> %d#, Connection %d#, Has Delegated Transaction, waiting to Dispose.\n", ObjectID, obj.ObjectID);
 | |
|             }
 | |
|             else {
 | |
|                 Bid.PoolerTrace("<prov.DbConnectionPool.DestroyObject|RES|CPOOL> %d#, Connection %d#, Removing from pool.\n", ObjectID, obj.ObjectID);
 | |
| 
 | |
|                 bool removed = false;
 | |
|                 lock (_objectList) {
 | |
|                     removed = _objectList.Remove(obj);
 | |
|                     Debug.Assert(removed, "attempt to DestroyObject not in list");
 | |
|                     _totalObjects = _objectList.Count;
 | |
|                 }
 | |
| 
 | |
|                 if (removed) {
 | |
|                     Bid.PoolerTrace("<prov.DbConnectionPool.DestroyObject|RES|CPOOL> %d#, Connection %d#, Removed from pool.\n", ObjectID, obj.ObjectID);
 | |
| #if !MOBILE
 | |
|                     PerformanceCounters.NumberOfPooledConnections.Decrement();
 | |
| #endif
 | |
|                 }
 | |
|                 obj.Dispose();
 | |
| 
 | |
|                 Bid.PoolerTrace("<prov.DbConnectionPool.DestroyObject|RES|CPOOL> %d#, Connection %d#, Disposed.\n", ObjectID, obj.ObjectID);
 | |
| #if !MOBILE
 | |
|                 PerformanceCounters.HardDisconnectsPerSecond.Increment();
 | |
| #endif
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         private void ErrorCallback(Object state) {
 | |
|             Bid.PoolerTrace("<prov.DbConnectionPool.ErrorCallback|RES|CPOOL> %d#, Resetting Error handling.\n", ObjectID);
 | |
| 
 | |
|             _errorOccurred = false;
 | |
|             _waitHandles.ErrorEvent.Reset();
 | |
| 
 | |
|             // the error state is cleaned, destroy the timer to avoid periodic invocation
 | |
|             Timer t     = _errorTimer;
 | |
|             _errorTimer = null;
 | |
|             if (t != null) {
 | |
|                 t.Dispose(); // Cancel timer request.
 | |
|             }
 | |
|         }
 | |
| 
 | |
| 
 | |
|         private Exception TryCloneCachedException()
 | |
|         // Cached exception can be of any type, so is not always cloneable.
 | |
|         // This functions clones SqlException 
 | |
|         // OleDb and Odbc connections are not passing throw this code
 | |
|         {   
 | |
|             if (_resError==null)
 | |
|                 return null;
 | |
|             if (_resError.GetType()==typeof(SqlClient.SqlException))
 | |
|                 return ((SqlClient.SqlException)_resError).InternalClone();
 | |
|             return _resError;
 | |
|         }
 | |
| 
 | |
|         void WaitForPendingOpen() {
 | |
| 
 | |
|             Debug.Assert(!Thread.CurrentThread.IsThreadPoolThread, "This thread may block for a long time.  Threadpool threads should not be used.");
 | |
| 
 | |
|             PendingGetConnection next;
 | |
| 
 | |
|             do {
 | |
|                 bool started = false;
 | |
| 
 | |
|                 RuntimeHelpers.PrepareConstrainedRegions();
 | |
|                 try {
 | |
| 
 | |
|                     RuntimeHelpers.PrepareConstrainedRegions();
 | |
|                     try { }
 | |
|                     finally {
 | |
|                         started = Interlocked.CompareExchange(ref _pendingOpensWaiting, 1, 0) == 0;
 | |
|                     }
 | |
| 
 | |
|                     if (!started) {
 | |
|                         return;
 | |
|                     }
 | |
| 
 | |
|                     while (_pendingOpens.TryDequeue(out next)) {
 | |
| 
 | |
|                         if (next.Completion.Task.IsCompleted) {
 | |
|                             continue;
 | |
|                         }
 | |
| 
 | |
|                         uint delay;
 | |
|                         if (next.DueTime == Timeout.Infinite) {
 | |
|                             delay = unchecked((uint)Timeout.Infinite);
 | |
|                         }
 | |
|                         else {
 | |
|                             delay = (uint)Math.Max(ADP.TimerRemainingMilliseconds(next.DueTime), 0);
 | |
|                         }
 | |
| 
 | |
|                         DbConnectionInternal connection = null;
 | |
|                         bool timeout = false;
 | |
|                         Exception caughtException = null;
 | |
| 
 | |
|                         RuntimeHelpers.PrepareConstrainedRegions();
 | |
|                         try {
 | |
| #if DEBUG
 | |
|                             System.Data.SqlClient.TdsParser.ReliabilitySection tdsReliabilitySection = new System.Data.SqlClient.TdsParser.ReliabilitySection();
 | |
| 
 | |
|                             RuntimeHelpers.PrepareConstrainedRegions();
 | |
|                             try {
 | |
|                                 tdsReliabilitySection.Start();
 | |
| #else
 | |
|                             {
 | |
| #endif //DEBUG
 | |
| 
 | |
|                                 bool allowCreate = true;
 | |
|                                 bool onlyOneCheckConnection = false;
 | |
|                                 ADP.SetCurrentTransaction(next.Completion.Task.AsyncState as Transactions.Transaction);
 | |
|                                 timeout = !TryGetConnection(next.Owner, delay, allowCreate, onlyOneCheckConnection, next.UserOptions, out connection);
 | |
|                             }
 | |
| #if DEBUG
 | |
|                             finally {
 | |
|                                 tdsReliabilitySection.Stop();
 | |
|                             }
 | |
| #endif //DEBUG
 | |
|                         }
 | |
|                         catch (System.OutOfMemoryException) {
 | |
|                             if (connection != null) { connection.DoomThisConnection(); }
 | |
|                             throw;
 | |
|                         }
 | |
|                         catch (System.StackOverflowException) {
 | |
|                             if (connection != null) { connection.DoomThisConnection(); }
 | |
|                             throw;
 | |
|                         }
 | |
|                         catch (System.Threading.ThreadAbortException) {
 | |
|                             if (connection != null) { connection.DoomThisConnection(); }
 | |
|                             throw;
 | |
|                         }
 | |
|                         catch (Exception e) {
 | |
|                             caughtException = e;
 | |
|                         }
 | |
| 
 | |
|                         if (caughtException != null) {
 | |
|                             next.Completion.TrySetException(caughtException);
 | |
|                         }
 | |
|                         else if (timeout) {
 | |
|                             next.Completion.TrySetException(ADP.ExceptionWithStackTrace(ADP.PooledOpenTimeout()));
 | |
|                         }
 | |
|                         else {
 | |
|                             Debug.Assert(connection != null, "connection should never be null in success case");
 | |
|                             if (!next.Completion.TrySetResult(connection)) {
 | |
|                                 // if the completion was cancelled, lets try and get this connection back for the next try
 | |
|                                 PutObject(connection, next.Owner);
 | |
|                             }
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
|                 finally {
 | |
|                     if (started) {
 | |
|                         Interlocked.Exchange(ref _pendingOpensWaiting, 0);
 | |
|                     }
 | |
|                 }
 | |
| 
 | |
|             } while (_pendingOpens.TryPeek(out next));
 | |
|         }
 | |
| 
 | |
|         internal bool TryGetConnection(DbConnection owningObject, TaskCompletionSource<DbConnectionInternal> retry, DbConnectionOptions userOptions, out DbConnectionInternal connection) {
 | |
| 
 | |
|             uint waitForMultipleObjectsTimeout = 0;
 | |
|             bool allowCreate = false;
 | |
| 
 | |
|             if (retry == null) {
 | |
|                 waitForMultipleObjectsTimeout = (uint)CreationTimeout;
 | |
| 
 | |
|                 // VSTFDEVDIV 445531: set the wait timeout to INFINITE (-1) if the SQL connection timeout is 0 (== infinite)
 | |
|                 if (waitForMultipleObjectsTimeout == 0)
 | |
|                     waitForMultipleObjectsTimeout = unchecked((uint)Timeout.Infinite);
 | |
| 
 | |
|                 allowCreate = true;
 | |
|             }
 | |
| 
 | |
|             if (_state != State.Running) {
 | |
|                 Bid.PoolerTrace("<prov.DbConnectionPool.GetConnection|RES|CPOOL> %d#, DbConnectionInternal State != Running.\n", ObjectID);
 | |
|                 connection = null;
 | |
|                 return true;
 | |
|             }
 | |
| 
 | |
|             bool onlyOneCheckConnection = true;
 | |
|             if (TryGetConnection(owningObject, waitForMultipleObjectsTimeout, allowCreate, onlyOneCheckConnection, userOptions, out connection)) {
 | |
|                 return true;
 | |
|             }
 | |
|             else if (retry == null) {
 | |
|                 // timed out on a sync call
 | |
|                 return true;
 | |
|             }
 | |
| 
 | |
|             var pendingGetConnection = 
 | |
|                 new PendingGetConnection(
 | |
|                     CreationTimeout == 0 ? Timeout.Infinite : ADP.TimerCurrent() + ADP.TimerFromSeconds(CreationTimeout/1000),
 | |
|                     owningObject,
 | |
|                     retry,
 | |
|                     userOptions);
 | |
|             _pendingOpens.Enqueue(pendingGetConnection);
 | |
| 
 | |
|             // it is better to StartNew too many times than not enough
 | |
|             if (_pendingOpensWaiting == 0) {
 | |
|                 Thread waitOpenThread = new Thread(WaitForPendingOpen);
 | |
|                 waitOpenThread.IsBackground = true;
 | |
|                 waitOpenThread.Start();
 | |
|             }
 | |
| 
 | |
|             connection = null;
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         [SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods")] // copied from Triaged.cs
 | |
|         [ResourceExposure(ResourceScope.None)] // SxS: this method does not expose resources
 | |
|         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
 | |
|         private bool TryGetConnection(DbConnection owningObject, uint waitForMultipleObjectsTimeout, bool allowCreate, bool onlyOneCheckConnection, DbConnectionOptions userOptions, out DbConnectionInternal connection) {
 | |
|             DbConnectionInternal obj = null;
 | |
|             SysTx.Transaction transaction = null;
 | |
| 
 | |
| #if !MOBILE
 | |
|             PerformanceCounters.SoftConnectsPerSecond.Increment();
 | |
| #endif
 | |
| 
 | |
|             Bid.PoolerTrace("<prov.DbConnectionPool.GetConnection|RES|CPOOL> %d#, Getting connection.\n", ObjectID);
 | |
| 
 | |
|             // If automatic transaction enlistment is required, then we try to
 | |
|             // get the connection from the transacted connection pool first.
 | |
|             if (HasTransactionAffinity) {
 | |
|                 obj = GetFromTransactedPool(out transaction);
 | |
|             }
 | |
| 
 | |
|             if (null == obj) {
 | |
|                 Interlocked.Increment(ref _waitCount);
 | |
|                 uint waitHandleCount = allowCreate ? 3u : 2u;
 | |
| 
 | |
|                 do {
 | |
|                     int waitResult = BOGUS_HANDLE;
 | |
|                     int releaseSemaphoreResult = 0;
 | |
| 
 | |
|                     bool mustRelease = false;
 | |
|                     int waitForMultipleObjectsExHR = 0;
 | |
|                     RuntimeHelpers.PrepareConstrainedRegions();
 | |
|                     try {
 | |
|                         _waitHandles.DangerousAddRef(ref mustRelease);
 | |
|                         
 | |
|                         // We absolutely must have the value of waitResult set, 
 | |
|                         // or we may leak the mutex in async abort cases.
 | |
|                         RuntimeHelpers.PrepareConstrainedRegions();
 | |
|                         try {
 | |
|                             Debug.Assert(2 == waitHandleCount || 3 == waitHandleCount, "unexpected waithandle count");
 | |
|                         } 
 | |
|                         finally {
 | |
|                             waitResult = SafeNativeMethods.WaitForMultipleObjectsEx(waitHandleCount, _waitHandles.DangerousGetHandle(), false, waitForMultipleObjectsTimeout, false);
 | |
| 
 | |
| #if !FULL_AOT_RUNTIME
 | |
|                             // VSTFDEVDIV 479551 - call GetHRForLastWin32Error immediately after after the native call
 | |
|                             if (waitResult == WAIT_FAILED) {
 | |
|                                 waitForMultipleObjectsExHR = Marshal.GetHRForLastWin32Error();
 | |
|                             }
 | |
| #endif
 | |
|                         }
 | |
| 
 | |
|                         // From the WaitAny docs: "If more than one object became signaled during
 | |
|                         // the call, this is the array index of the signaled object with the
 | |
|                         // smallest index value of all the signaled objects."  This is important
 | |
|                         // so that the free object signal will be returned before a creation
 | |
|                         // signal.
 | |
| 
 | |
|                         switch (waitResult) {
 | |
|                         case WAIT_TIMEOUT:
 | |
|                             Bid.PoolerTrace("<prov.DbConnectionPool.GetConnection|RES|CPOOL> %d#, Wait timed out.\n", ObjectID);
 | |
|                             Interlocked.Decrement(ref _waitCount);
 | |
|                             connection = null;
 | |
|                             return false;
 | |
| 
 | |
|                         case ERROR_HANDLE:
 | |
|                             // Throw the error that PoolCreateRequest stashed.
 | |
|                             Bid.PoolerTrace("<prov.DbConnectionPool.GetConnection|RES|CPOOL> %d#, Errors are set.\n", ObjectID);
 | |
|                             Interlocked.Decrement(ref _waitCount);
 | |
|                             throw TryCloneCachedException();                                            
 | |
| 
 | |
|                         case CREATION_HANDLE:
 | |
|                             Bid.PoolerTrace("<prov.DbConnectionPool.GetConnection|RES|CPOOL> %d#, Creating new connection.\n", ObjectID);
 | |
| 
 | |
|                             try {
 | |
|                                 obj = UserCreateRequest(owningObject, userOptions);
 | |
|                             }
 | |
|                             catch {
 | |
|                                 if (null == obj) {
 | |
|                                     Interlocked.Decrement(ref _waitCount);
 | |
|                                 }
 | |
|                                 throw;
 | |
|                             }
 | |
|                             finally {
 | |
|                                 // SQLBUDT #386664 - ensure that we release this waiter, regardless
 | |
|                                 // of any exceptions that may be thrown.
 | |
|                                 if (null != obj) {
 | |
|                                     Interlocked.Decrement(ref _waitCount);
 | |
|                                 }
 | |
|                             }
 | |
| 
 | |
|                             if (null == obj) {
 | |
|                                 // If we were not able to create an object, check to see if
 | |
|                                 // we reached MaxPoolSize.  If so, we will no longer wait on
 | |
|                                 // the CreationHandle, but instead wait for a free object or
 | |
|                                 // the timeout.
 | |
| 
 | |
|                                 // 
 | |
| 
 | |
|                                 if (Count >= MaxPoolSize && 0 != MaxPoolSize) {
 | |
|                                     if (!ReclaimEmancipatedObjects()) {
 | |
|                                         // modify handle array not to wait on creation mutex anymore
 | |
|                                         Debug.Assert(2 == CREATION_HANDLE, "creation handle changed value");
 | |
|                                         waitHandleCount = 2;
 | |
|                                     }
 | |
|                                 }
 | |
|                             }
 | |
|                             break;
 | |
| 
 | |
|                         case SEMAPHORE_HANDLE:
 | |
|                             //
 | |
|                             //    guaranteed available inventory
 | |
|                             //
 | |
|                             Interlocked.Decrement(ref _waitCount);
 | |
|                             obj = GetFromGeneralPool();
 | |
| 
 | |
|                             if ((obj != null) && (!obj.IsConnectionAlive())) {
 | |
|                                 Bid.PoolerTrace("<prov.DbConnectionPool.GetConnection|RES|CPOOL> %d#, Connection %d#, found dead and removed.\n", ObjectID, obj.ObjectID);
 | |
|                                 DestroyObject(obj);
 | |
|                                 obj = null;     // Setting to null in case creating a new object fails
 | |
| 
 | |
|                                 if (onlyOneCheckConnection) {
 | |
|                                     if (_waitHandles.CreationSemaphore.WaitOne(unchecked((int)waitForMultipleObjectsTimeout))) {
 | |
|                                         RuntimeHelpers.PrepareConstrainedRegions();
 | |
|                                         try {
 | |
|                                             Bid.PoolerTrace("<prov.DbConnectionPool.GetConnection|RES|CPOOL> %d#, Creating new connection.\n", ObjectID);
 | |
|                                             obj = UserCreateRequest(owningObject, userOptions);
 | |
|                                         }
 | |
|                                         finally {
 | |
|                                             _waitHandles.CreationSemaphore.Release(1);
 | |
|                                         }
 | |
|                                     }
 | |
|                                     else {
 | |
|                                         // Timeout waiting for creation semaphore - return null
 | |
|                                         Bid.PoolerTrace("<prov.DbConnectionPool.GetConnection|RES|CPOOL> %d#, Wait timed out.\n", ObjectID);
 | |
|                                         connection = null;
 | |
|                                         return false;
 | |
|                                     }
 | |
|                                 }
 | |
|                             }
 | |
|                             break;
 | |
|                             
 | |
|                         case WAIT_FAILED:
 | |
|                             Debug.Assert(waitForMultipleObjectsExHR != 0, "WaitForMultipleObjectsEx failed but waitForMultipleObjectsExHR remained 0");
 | |
|                             Bid.PoolerTrace("<prov.DbConnectionPool.GetConnection|RES|CPOOL> %d#, Wait failed.\n", ObjectID);
 | |
|                             Interlocked.Decrement(ref _waitCount);
 | |
|                             Marshal.ThrowExceptionForHR(waitForMultipleObjectsExHR);
 | |
|                             goto default; // if ThrowExceptionForHR didn't throw for some reason
 | |
|                         case (WAIT_ABANDONED+SEMAPHORE_HANDLE):
 | |
|                             Bid.PoolerTrace("<prov.DbConnectionPool.GetConnection|RES|CPOOL> %d#, Semaphore handle abandonded.\n", ObjectID);
 | |
|                             Interlocked.Decrement(ref _waitCount);
 | |
|                             throw new AbandonedMutexException(SEMAPHORE_HANDLE,_waitHandles.PoolSemaphore);
 | |
|                         case (WAIT_ABANDONED+ERROR_HANDLE):
 | |
|                             Bid.PoolerTrace("<prov.DbConnectionPool.GetConnection|RES|CPOOL> %d#, Error handle abandonded.\n", ObjectID);
 | |
|                             Interlocked.Decrement(ref _waitCount);
 | |
|                             throw new AbandonedMutexException(ERROR_HANDLE,_waitHandles.ErrorEvent);
 | |
|                         case (WAIT_ABANDONED+CREATION_HANDLE):
 | |
|                             Bid.PoolerTrace("<prov.DbConnectionPool.GetConnection|RES|CPOOL> %d#, Creation handle abandoned.\n", ObjectID);
 | |
|                             Interlocked.Decrement(ref _waitCount);
 | |
|                             throw new AbandonedMutexException(CREATION_HANDLE,_waitHandles.CreationSemaphore);
 | |
|                         default:
 | |
|                             Bid.PoolerTrace("<prov.DbConnectionPool.GetConnection|RES|CPOOL> %d#, WaitForMultipleObjects=%d\n", ObjectID, waitResult);
 | |
|                             Interlocked.Decrement(ref _waitCount);
 | |
|                             throw ADP.InternalError(ADP.InternalErrorCode.UnexpectedWaitAnyResult);
 | |
|                         }
 | |
|                     }
 | |
|                     finally {
 | |
|                         if (CREATION_HANDLE == waitResult) {
 | |
|                             int result = SafeNativeMethods.ReleaseSemaphore(_waitHandles.CreationHandle.DangerousGetHandle(), 1, IntPtr.Zero);
 | |
|                             if (0 == result) { // failure case
 | |
| #if !FULL_AOT_RUNTIME
 | |
|                                 releaseSemaphoreResult = Marshal.GetHRForLastWin32Error();
 | |
| #endif
 | |
|                             }
 | |
|                         }
 | |
|                         if (mustRelease) {
 | |
|                             _waitHandles.DangerousRelease();
 | |
|                         }
 | |
|                     }
 | |
|                     if (0 != releaseSemaphoreResult) {
 | |
|                         Marshal.ThrowExceptionForHR(releaseSemaphoreResult); // will only throw if (hresult < 0)
 | |
|                     }
 | |
|                 } while (null == obj);
 | |
|             }
 | |
| 
 | |
|             if (null != obj)
 | |
|             {
 | |
|                 PrepareConnection(owningObject, obj, transaction);
 | |
|             }
 | |
| 
 | |
|             connection = obj;
 | |
|             return true;
 | |
|         }
 | |
| 
 | |
|         private void PrepareConnection(DbConnection owningObject, DbConnectionInternal obj, SysTx.Transaction transaction) {
 | |
|             lock (obj)
 | |
|             {   // Protect against Clear and ReclaimEmancipatedObjects, which call IsEmancipated, which is affected by PrePush and PostPop
 | |
|                 obj.PostPop(owningObject);
 | |
|             }
 | |
|             try
 | |
|             {
 | |
|                 obj.ActivateConnection(transaction);
 | |
|             }
 | |
|             catch
 | |
|             {
 | |
|                 // if Activate throws an exception
 | |
|                 // put it back in the pool or have it properly disposed of
 | |
|                 this.PutObject(obj, owningObject);
 | |
|                 throw;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Creates a new connection to replace an existing connection
 | |
|         /// </summary>
 | |
|         /// <param name="owningObject">Outer connection that currently owns <paramref name="oldConnection"/></param>
 | |
|         /// <param name="userOptions">Options used to create the new connection</param>
 | |
|         /// <param name="oldConnection">Inner connection that will be replaced</param>
 | |
|         /// <returns>A new inner connection that is attached to the <paramref name="owningObject"/></returns>
 | |
|         internal DbConnectionInternal ReplaceConnection(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection) {
 | |
| #if !MOBILE
 | |
|             PerformanceCounters.SoftConnectsPerSecond.Increment();
 | |
| #endif
 | |
|             Bid.PoolerTrace("<prov.DbConnectionPool.ReplaceConnection|RES|CPOOL> %d#, replacing connection.\n", ObjectID);
 | |
| 
 | |
|             DbConnectionInternal newConnection = UserCreateRequest(owningObject, userOptions, oldConnection);
 | |
|             
 | |
|             if (newConnection != null) {
 | |
|                 PrepareConnection(owningObject, newConnection, oldConnection.EnlistedTransaction);
 | |
|                 oldConnection.PrepareForReplaceConnection();
 | |
|                 oldConnection.DeactivateConnection();
 | |
|                 oldConnection.Dispose();
 | |
|             }
 | |
| 
 | |
|             return newConnection;
 | |
|         }
 | |
| 
 | |
|         private DbConnectionInternal GetFromGeneralPool() {
 | |
|             DbConnectionInternal obj = null;
 | |
| 
 | |
|             if (!_stackNew.TryPop(out obj)) {
 | |
|                 if (!_stackOld.TryPop(out obj)) {
 | |
|                     obj = null;
 | |
|                 }
 | |
|                 else {
 | |
|                     Debug.Assert(obj != null, "null connection is not expected");
 | |
|                 }
 | |
|             }
 | |
|             else {
 | |
|                 Debug.Assert(obj != null, "null connection is not expected");
 | |
|             }
 | |
| 
 | |
|             // SQLBUDT #356870 -- When another thread is clearing this pool,  
 | |
|             // it will remove all connections in this pool which causes the 
 | |
|             // following assert to fire, which really mucks up stress against
 | |
|             //  checked bits.  The assert is benign, so we're commenting it out.  
 | |
|             //Debug.Assert(obj != null, "GetFromGeneralPool called with nothing in the pool!");
 | |
| 
 | |
|             if (null != obj) {
 | |
|                 Bid.PoolerTrace("<prov.DbConnectionPool.GetFromGeneralPool|RES|CPOOL> %d#, Connection %d#, Popped from general pool.\n", ObjectID, obj.ObjectID);
 | |
| #if !MOBILE
 | |
|                 PerformanceCounters.NumberOfFreeConnections.Decrement();
 | |
| #endif
 | |
|             }
 | |
|             return(obj);
 | |
|         }
 | |
| 
 | |
|         private DbConnectionInternal GetFromTransactedPool(out SysTx.Transaction transaction) {
 | |
|             transaction = ADP.GetCurrentTransaction();
 | |
|             DbConnectionInternal obj = null;
 | |
| 
 | |
|             if (null != transaction && null != _transactedConnectionPool) {
 | |
|                 obj = _transactedConnectionPool.GetTransactedObject(transaction);
 | |
| 
 | |
|                 if (null != obj) {
 | |
|                     Bid.PoolerTrace("<prov.DbConnectionPool.GetFromTransactedPool|RES|CPOOL> %d#, Connection %d#, Popped from transacted pool.\n", ObjectID, obj.ObjectID);
 | |
| #if !MOBILE
 | |
|                     PerformanceCounters.NumberOfFreeConnections.Decrement();
 | |
| #endif
 | |
| 
 | |
|                     if (obj.IsTransactionRoot) {
 | |
|                         try {
 | |
|                             obj.IsConnectionAlive(true);
 | |
|                         }
 | |
|                         catch {
 | |
|                             Bid.PoolerTrace("<prov.DbConnectionPool.GetFromTransactedPool|RES|CPOOL> %d#, Connection %d#, found dead and removed.\n", ObjectID, obj.ObjectID);
 | |
|                             DestroyObject(obj);
 | |
|                             throw;
 | |
|                         }
 | |
|                     }
 | |
|                     else if (!obj.IsConnectionAlive()) {
 | |
|                         Bid.PoolerTrace("<prov.DbConnectionPool.GetFromTransactedPool|RES|CPOOL> %d#, Connection %d#, found dead and removed.\n", ObjectID, obj.ObjectID);
 | |
|                         DestroyObject(obj);
 | |
|                         obj = null;
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|             return obj;
 | |
|         }
 | |
| 
 | |
|         [ResourceExposure(ResourceScope.None)] // SxS: this method does not expose resources
 | |
|         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
 | |
|         private void PoolCreateRequest(object state) {
 | |
|             // called by pooler to ensure pool requests are currently being satisfied -
 | |
|             // creation mutex has not been obtained
 | |
| 
 | |
|             IntPtr hscp;
 | |
| 
 | |
|             Bid.PoolerScopeEnter(out hscp, "<prov.DbConnectionPool.PoolCreateRequest|RES|INFO|CPOOL> %d#\n", ObjectID);
 | |
| 
 | |
|             try {
 | |
|                 if (State.Running == _state) {
 | |
| 
 | |
|                     // in case WaitForPendingOpen ever failed with no subsequent OpenAsync calls,
 | |
|                     // start it back up again
 | |
|                     if (!_pendingOpens.IsEmpty && _pendingOpensWaiting == 0) {
 | |
|                         Thread waitOpenThread = new Thread(WaitForPendingOpen);
 | |
|                         waitOpenThread.IsBackground = true;
 | |
|                         waitOpenThread.Start();
 | |
|                     }
 | |
| 
 | |
|                     // Before creating any new objects, reclaim any released objects that were
 | |
|                     // not closed.
 | |
|                     ReclaimEmancipatedObjects();
 | |
| 
 | |
|                     if (!ErrorOccurred) {
 | |
|                         if (NeedToReplenish) {
 | |
|                             // Check to see if pool was created using integrated security and if so, make
 | |
|                             // sure the identity of current user matches that of user that created pool.
 | |
|                             // If it doesn't match, do not create any objects on the ThreadPool thread,
 | |
|                             // since either Open will fail or we will open a object for this pool that does
 | |
|                             // not belong in this pool.  The side effect of this is that if using integrated
 | |
|                             // security min pool size cannot be guaranteed.
 | |
|                             if (UsingIntegrateSecurity && !_identity.Equals(DbConnectionPoolIdentity.GetCurrent())) {
 | |
|                                 return;
 | |
|                             }
 | |
|                             bool mustRelease = false;
 | |
|                             int waitResult = BOGUS_HANDLE;
 | |
|                             uint timeout = (uint)CreationTimeout;
 | |
| 
 | |
|                             RuntimeHelpers.PrepareConstrainedRegions();
 | |
|                             try {
 | |
|                                 _waitHandles.DangerousAddRef(ref mustRelease);
 | |
|                                 
 | |
|                                 // Obtain creation mutex so we're the only one creating objects
 | |
|                                 // and we must have the wait result
 | |
|                                 RuntimeHelpers.PrepareConstrainedRegions();
 | |
|                                 try { } finally {
 | |
|                                     waitResult = SafeNativeMethods.WaitForSingleObjectEx(_waitHandles.CreationHandle.DangerousGetHandle(), timeout, false);
 | |
|                                 }
 | |
|                                 if (WAIT_OBJECT_0 == waitResult) {
 | |
|                                     DbConnectionInternal newObj;
 | |
| 
 | |
|                                     // Check ErrorOccurred again after obtaining mutex
 | |
|                                     if (!ErrorOccurred) {
 | |
|                                         while (NeedToReplenish) {
 | |
|                                             // Don't specify any user options because there is no outer connection associated with the new connection
 | |
|                                             newObj = CreateObject(owningObject: null, userOptions: null, oldConnection: null);
 | |
| 
 | |
|                                             // We do not need to check error flag here, since we know if
 | |
|                                             // CreateObject returned null, we are in error case.
 | |
|                                             if (null != newObj) {
 | |
|                                                 PutNewObject(newObj);
 | |
|                                             }
 | |
|                                             else {
 | |
|                                                 break;
 | |
|                                             }
 | |
|                                         }
 | |
|                                     }
 | |
|                                 }
 | |
|                                 else if (WAIT_TIMEOUT == waitResult) {
 | |
|                                     // do not wait forever and potential block this worker thread
 | |
|                                     // instead wait for a period of time and just requeue to try again
 | |
|                                     QueuePoolCreateRequest();
 | |
|                                 }
 | |
|                                 else {
 | |
|                                     // trace waitResult and ignore the failure
 | |
|                                     Bid.PoolerTrace("<prov.DbConnectionPool.PoolCreateRequest|RES|CPOOL> %d#, PoolCreateRequest called WaitForSingleObject failed %d", ObjectID, waitResult);
 | |
|                                 }
 | |
|                             }
 | |
|                             catch (Exception e) {
 | |
|                                 // 
 | |
|                                 if (!ADP.IsCatchableExceptionType(e)) {
 | |
|                                     throw;
 | |
|                                 }
 | |
| 
 | |
|                                 // Now that CreateObject can throw, we need to catch the exception and discard it.
 | |
|                                 // There is no further action we can take beyond tracing.  The error will be 
 | |
|                                 // thrown to the user the next time they request a connection.
 | |
|                                 Bid.PoolerTrace("<prov.DbConnectionPool.PoolCreateRequest|RES|CPOOL> %d#, PoolCreateRequest called CreateConnection which threw an exception: %ls", ObjectID, e);
 | |
|                             }
 | |
|                             finally {
 | |
|                                 if (WAIT_OBJECT_0 == waitResult) {
 | |
|                                     // reuse waitResult and ignore its value
 | |
|                                     waitResult = SafeNativeMethods.ReleaseSemaphore(_waitHandles.CreationHandle.DangerousGetHandle(), 1, IntPtr.Zero);
 | |
|                                 }
 | |
|                                 if (mustRelease) {
 | |
|                                     _waitHandles.DangerousRelease();
 | |
|                                 }
 | |
|                             }
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|             finally {
 | |
|                 Bid.ScopeLeave(ref hscp);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         internal void PutNewObject(DbConnectionInternal obj) {
 | |
|             Debug.Assert(null != obj,        "why are we adding a null object to the pool?");
 | |
| 
 | |
|             // VSTFDEVDIV 742887 - When another thread is clearing this pool, it
 | |
|             // will set _cannotBePooled for all connections in this pool without prejudice which
 | |
|             // causes the following assert to fire, which really mucks up stress
 | |
|             // against checked bits.
 | |
|             // Debug.Assert(obj.CanBePooled,    "non-poolable object in pool");
 | |
| 
 | |
|             Bid.PoolerTrace("<prov.DbConnectionPool.PutNewObject|RES|CPOOL> %d#, Connection %d#, Pushing to general pool.\n", ObjectID, obj.ObjectID);
 | |
| 
 | |
|             _stackNew.Push(obj);
 | |
|             _waitHandles.PoolSemaphore.Release(1);
 | |
| #if !MOBILE
 | |
|             PerformanceCounters.NumberOfFreeConnections.Increment();
 | |
| #endif
 | |
|         }
 | |
| 
 | |
|         internal void PutObject(DbConnectionInternal obj, object owningObject) {
 | |
|             Debug.Assert(null != obj, "null obj?");
 | |
| 
 | |
| #if !MOBILE
 | |
|             PerformanceCounters.SoftDisconnectsPerSecond.Increment();
 | |
| #endif
 | |
| 
 | |
|             // Once a connection is closing (which is the state that we're in at
 | |
|             // this point in time) you cannot delegate a transaction to or enlist
 | |
|             // a transaction in it, so we can correctly presume that if there was
 | |
|             // not a delegated or enlisted transaction to start with, that there
 | |
|             // will not be a delegated or enlisted transaction once we leave the
 | |
|             // lock.
 | |
| 
 | |
|             lock (obj) {
 | |
|                 // Calling PrePush prevents the object from being reclaimed
 | |
|                 // once we leave the lock, because it sets _pooledCount such
 | |
|                 // that it won't appear to be out of the pool.  What that
 | |
|                 // means, is that we're now responsible for this connection:
 | |
|                 // it won't get reclaimed if we drop the ball somewhere.
 | |
|                 obj.PrePush(owningObject);
 | |
| 
 | |
|                 // 
 | |
|             }
 | |
| 
 | |
|             DeactivateObject(obj);
 | |
|         }
 | |
| 
 | |
|         internal void PutObjectFromTransactedPool(DbConnectionInternal obj) {
 | |
|             Debug.Assert(null != obj, "null pooledObject?");
 | |
|             Debug.Assert(obj.EnlistedTransaction == null, "pooledObject is still enlisted?");
 | |
| 
 | |
|             // called by the transacted connection pool , once it's removed the
 | |
|             // connection from it's list.  We put the connection back in general
 | |
|             // circulation.
 | |
| 
 | |
|             // NOTE: there is no locking required here because if we're in this
 | |
|             // method, we can safely presume that the caller is the only person
 | |
|             // that is using the connection, and that all pre-push logic has been
 | |
|             // done and all transactions are ended.
 | |
| 
 | |
|             Bid.PoolerTrace("<prov.DbConnectionPool.PutObjectFromTransactedPool|RES|CPOOL> %d#, Connection %d#, Transaction has ended.\n", ObjectID, obj.ObjectID);
 | |
| 
 | |
|             if (_state == State.Running && obj.CanBePooled) {
 | |
|                 PutNewObject(obj);
 | |
|             }
 | |
|             else {
 | |
|                 DestroyObject(obj);
 | |
|                 QueuePoolCreateRequest();
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         private void QueuePoolCreateRequest() {
 | |
|             if (State.Running == _state) {
 | |
|                 // Make sure we're at quota by posting a callback to the threadpool.
 | |
|                 ThreadPool.QueueUserWorkItem(_poolCreateRequest);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         private bool ReclaimEmancipatedObjects() {
 | |
|             bool emancipatedObjectFound = false;
 | |
| 
 | |
|             Bid.PoolerTrace("<prov.DbConnectionPool.ReclaimEmancipatedObjects|RES|CPOOL> %d#\n", ObjectID);
 | |
| 
 | |
|             List<DbConnectionInternal> reclaimedObjects = new List<DbConnectionInternal>();
 | |
|             int count;
 | |
| 
 | |
|             lock(_objectList) {
 | |
|                 count = _objectList.Count;
 | |
| 
 | |
|                 for (int i = 0; i < count; ++i) {
 | |
|                     DbConnectionInternal obj = _objectList[i];
 | |
| 
 | |
|                     if (null != obj) {
 | |
|                         bool locked = false;
 | |
| 
 | |
|                         try {
 | |
|                             Monitor.TryEnter(obj, ref locked);
 | |
| 
 | |
|                             if (locked) { // avoid race condition with PrePush/PostPop and IsEmancipated
 | |
|                                 if (obj.IsEmancipated) {
 | |
|                                     // Inside the lock, we want to do as little
 | |
|                                     // as possible, so we simply mark the object
 | |
|                                     // as being in the pool, but hand it off to
 | |
|                                     // an out of pool list to be deactivated,
 | |
|                                     // etc.
 | |
|                                     obj.PrePush(null);
 | |
|                                     reclaimedObjects.Add(obj);
 | |
|                                 }
 | |
|                             }
 | |
|                         }
 | |
|                         finally {
 | |
|                             if (locked)
 | |
|                                 Monitor.Exit(obj);
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             // NOTE: we don't want to call DeactivateObject while we're locked,
 | |
|             // because it can make roundtrips to the server and this will block
 | |
|             // object creation in the pooler.  Instead, we queue things we need
 | |
|             // to do up, and process them outside the lock.
 | |
|             count = reclaimedObjects.Count;
 | |
| 
 | |
|             for (int i = 0; i < count; ++i) {
 | |
|                 DbConnectionInternal obj = reclaimedObjects[i];
 | |
| 
 | |
|                 Bid.PoolerTrace("<prov.DbConnectionPool.ReclaimEmancipatedObjects|RES|CPOOL> %d#, Connection %d#, Reclaiming.\n", ObjectID, obj.ObjectID);
 | |
| #if !MOBILE
 | |
|                 PerformanceCounters.NumberOfReclaimedConnections.Increment();
 | |
| #endif
 | |
| 
 | |
|                 emancipatedObjectFound = true;
 | |
| 
 | |
|                 obj.DetachCurrentTransactionIfEnded();
 | |
|                 DeactivateObject(obj);
 | |
|             }
 | |
|             return emancipatedObjectFound;
 | |
|         }
 | |
| 
 | |
|         internal void Startup() {
 | |
|             Bid.PoolerTrace("<prov.DbConnectionPool.Startup|RES|INFO|CPOOL> %d#, CleanupWait=%d\n", ObjectID, _cleanupWait);
 | |
| 
 | |
|             _cleanupTimer = CreateCleanupTimer();
 | |
|             if (NeedToReplenish) {
 | |
|                 QueuePoolCreateRequest();
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         internal void Shutdown() {
 | |
|             Bid.PoolerTrace("<prov.DbConnectionPool.Shutdown|RES|INFO|CPOOL> %d#\n", ObjectID);
 | |
| 
 | |
|             _state = State.ShuttingDown;
 | |
| 
 | |
|             // deactivate timer callbacks
 | |
|             Timer t = _cleanupTimer;
 | |
|             _cleanupTimer = null;
 | |
|             if (null != t) {
 | |
|                 t.Dispose();
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         // TransactionEnded merely provides the plumbing for DbConnectionInternal to access the transacted pool
 | |
|         //   that is implemented inside DbConnectionPool. This method's counterpart (PutTransactedObject) should
 | |
|         //   only be called from DbConnectionPool.DeactivateObject and thus the plumbing to provide access to 
 | |
|         //   other objects is unnecessary (hence the asymmetry of Ended but no Begin)
 | |
|         internal void TransactionEnded(SysTx.Transaction transaction, DbConnectionInternal transactedObject) {
 | |
|             Debug.Assert(null != transaction, "null transaction?");
 | |
|             Debug.Assert(null != transactedObject, "null transactedObject?");
 | |
|             // Note: connection may still be associated with transaction due to Explicit Unbinding requirement.
 | |
| 
 | |
|             Bid.PoolerTrace("<prov.DbConnectionPool.TransactionEnded|RES|CPOOL> %d#, Transaction %d#, Connection %d#, Transaction Completed\n", ObjectID, transaction.GetHashCode(), transactedObject.ObjectID);
 | |
| 
 | |
|             // called by the internal connection when it get's told that the
 | |
|             // transaction is completed.  We tell the transacted pool to remove
 | |
|             // the connection from it's list, then we put the connection back in
 | |
|             // general circulation.
 | |
| 
 | |
|             TransactedConnectionPool transactedConnectionPool = _transactedConnectionPool;
 | |
|             if (null != transactedConnectionPool) {
 | |
|                 transactedConnectionPool.TransactionEnded(transaction, transactedObject);
 | |
|             }
 | |
|         }
 | |
|         
 | |
| 
 | |
|         private DbConnectionInternal UserCreateRequest(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection = null) {
 | |
|             // called by user when they were not able to obtain a free object but
 | |
|             // instead obtained creation mutex
 | |
| 
 | |
|             DbConnectionInternal obj = null;
 | |
|             if (ErrorOccurred) {
 | |
|                 throw TryCloneCachedException();
 | |
|             }
 | |
|             else {
 | |
|                  if ((oldConnection != null) || (Count < MaxPoolSize) || (0 == MaxPoolSize)) {
 | |
|                     // If we have an odd number of total objects, reclaim any dead objects.
 | |
|                     // If we did not find any objects to reclaim, create a new one.
 | |
| 
 | |
|                     // 
 | |
|                      if ((oldConnection != null) || (Count & 0x1) == 0x1 || !ReclaimEmancipatedObjects())
 | |
|                         obj = CreateObject(owningObject, userOptions, oldConnection);
 | |
|                 }
 | |
|                 return obj;
 | |
|             } 
 | |
|         }
 | |
|     }
 | |
| }
 |