Code:
/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / Data / System / Data / ProviderBase / DbConnectionFactory.cs / 1305376 / DbConnectionFactory.cs
//------------------------------------------------------------------------------ //// Copyright (c) Microsoft Corporation. All rights reserved. // //[....] //[....] //----------------------------------------------------------------------------- namespace System.Data.ProviderBase { using System; using System.Collections.Generic; using System.Diagnostics; using System.Data.Common; using System.Threading; #if ORACLE using System.Data.OracleClient; #endif internal abstract class DbConnectionFactory { private Dictionary_connectionPoolGroups; private readonly List _poolsToRelease; private readonly List _poolGroupsToRelease; private readonly DbConnectionPoolCounters _performanceCounters; private readonly Timer _pruningTimer; private const int PruningDueTime =4*60*1000; // 4 minutes private const int PruningPeriod = 30*1000; // thirty seconds private static int _objectTypeCount; // Bid counter internal readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); protected DbConnectionFactory() : this (DbConnectionPoolCountersNoCounters.SingletonInstance) { } protected DbConnectionFactory(DbConnectionPoolCounters performanceCounters) { _performanceCounters = performanceCounters; _connectionPoolGroups = new Dictionary (); _poolsToRelease = new List (); _poolGroupsToRelease = new List (); _pruningTimer = CreatePruningTimer(); } internal DbConnectionPoolCounters PerformanceCounters { get { return _performanceCounters; } } abstract public DbProviderFactory ProviderFactory { get; } internal int ObjectID { get { return _objectID; } } public void ClearAllPools() { IntPtr hscp; Bid.ScopeEnter(out hscp, " "); try { Dictionary connectionPoolGroups = _connectionPoolGroups; foreach (KeyValuePair entry in connectionPoolGroups) { DbConnectionPoolGroup poolGroup = entry.Value; if (null != poolGroup) { Debug.Assert(!poolGroup.IsDisabled, "Disabled pool entry discovered"); poolGroup.Clear(); } } } finally { Bid.ScopeLeave(ref hscp); } } public void ClearPool(DbConnection connection) { ADP.CheckArgumentNull(connection, "connection"); IntPtr hscp; Bid.ScopeEnter(out hscp, " %d#" , GetObjectId(connection)); try { DbConnectionPoolGroup poolGroup = GetConnectionPoolGroup(connection); if (null != poolGroup) { poolGroup.Clear(); } } finally { Bid.ScopeLeave(ref hscp); } } #if !ORACLE public void ClearPool(string connectionString) { ADP.CheckArgumentNull(connectionString, "connectionString"); IntPtr hscp; Bid.ScopeEnter(out hscp, " connectionString"); try { DbConnectionPoolGroup poolGroup; Dictionary connectionPoolGroups = _connectionPoolGroups; if (connectionPoolGroups.TryGetValue(connectionString, out poolGroup)) { poolGroup.Clear(); } } finally { Bid.ScopeLeave(ref hscp); } } #endif internal virtual DbConnectionPoolProviderInfo CreateConnectionPoolProviderInfo(DbConnectionOptions connectionOptions){ return null; } virtual protected DbMetaDataFactory CreateMetaDataFactory(DbConnectionInternal internalConnection, out bool cacheMetaDataFactory) { // providers that support GetSchema must override this with a method that creates a meta data // factory appropriate for them. cacheMetaDataFactory = false; throw ADP.NotSupported(); } internal DbConnectionInternal CreateNonPooledConnection(DbConnection owningConnection, DbConnectionPoolGroup poolGroup) { Debug.Assert(null != owningConnection, "null owningConnection?"); Debug.Assert(null != poolGroup, "null poolGroup?"); DbConnectionOptions connectionOptions = poolGroup.ConnectionOptions; DbConnectionPoolGroupProviderInfo poolGroupProviderInfo = poolGroup.ProviderInfo; DbConnectionInternal newConnection = CreateConnection(connectionOptions, poolGroupProviderInfo, null, owningConnection); if (null != newConnection) { PerformanceCounters.HardConnectsPerSecond.Increment(); newConnection.MakeNonPooledObject(owningConnection, PerformanceCounters); } Bid.Trace(" %d#, Non-pooled database connection created.\n", ObjectID); return newConnection; } internal DbConnectionInternal CreatePooledConnection(DbConnection owningConnection, DbConnectionPool pool, DbConnectionOptions options) { Debug.Assert(null != pool, "null pool?"); DbConnectionPoolGroupProviderInfo poolGroupProviderInfo = pool.PoolGroup.ProviderInfo; DbConnectionInternal newConnection = CreateConnection(options, poolGroupProviderInfo, pool, owningConnection); if (null != newConnection) { PerformanceCounters.HardConnectsPerSecond.Increment(); newConnection.MakePooledConnection(pool); } Bid.Trace(" %d#, Pooled database connection created.\n", ObjectID); return newConnection; } virtual internal DbConnectionPoolGroupProviderInfo CreateConnectionPoolGroupProviderInfo (DbConnectionOptions connectionOptions) { return null; } private Timer CreatePruningTimer() { TimerCallback callback = new TimerCallback(PruneConnectionPoolGroups); return new Timer(callback, null, PruningDueTime, PruningPeriod); } #if !ORACLE protected DbConnectionOptions FindConnectionOptions(string connectionString) { if (!ADP.IsEmpty(connectionString)) { DbConnectionPoolGroup connectionPoolGroup; Dictionary connectionPoolGroups = _connectionPoolGroups; if (connectionPoolGroups.TryGetValue(connectionString, out connectionPoolGroup)) { return connectionPoolGroup.ConnectionOptions; } } return null; } #endif internal DbConnectionInternal GetConnection(DbConnection owningConnection) { Debug.Assert(null != owningConnection, "null owningConnection?"); DbConnectionPoolGroup poolGroup; DbConnectionPool connectionPool; DbConnectionInternal connection; // SQLBU 431251: // Work around race condition with clearing the pool between GetConnectionPool obtaining pool // and GetConnection on the pool checking the pool state. Clearing the pool in this window // will switch the pool into the ShuttingDown state, and GetConnection will return null. // There is probably a better solution involving locking the pool/group, but that entails a major // re-design of the connection pooling synchronization, so is post-poned for now. // VSDD 674236: use retriesLeft to prevent CPU spikes with incremental sleep // start with one msec, double the time every retry // max time is: 1 + 2 + 4 + ... + 2^(retries-1) == 2^retries -1 == 1023ms (for 10 retries) int retriesLeft = 10; int timeBetweenRetriesMilliseconds = 1; do { poolGroup = GetConnectionPoolGroup(owningConnection); connectionPool = GetConnectionPool(owningConnection, poolGroup); if (null == connectionPool) { // If GetConnectionPool returns null, we can be certain that // this connection should not be pooled via DbConnectionPool // or have a disabled pool entry. poolGroup = GetConnectionPoolGroup(owningConnection); // previous entry have been disabled connection = CreateNonPooledConnection(owningConnection, poolGroup); PerformanceCounters.NumberOfNonPooledConnections.Increment(); } else { connection = connectionPool.GetConnection(owningConnection); if (connection == null) { // connection creation failed on semaphore waiting or if max pool reached if (connectionPool.IsRunning) { // If GetConnection failed while the pool is running, the pool timeout occurred. Bid.Trace(" %d#, GetConnection failed because a pool timeout occurred.\n", ObjectID); throw ADP.PooledOpenTimeout(); } else { // We've hit the race condition, where the pool was shut down after we got it from the group. // Yield time slice to allow shut down activities to complete and a new, running pool to be instantiated // before retrying. Threading.Thread.Sleep(timeBetweenRetriesMilliseconds); timeBetweenRetriesMilliseconds *= 2; // double the wait time for next iteration } } } } while (connection == null && retriesLeft-- > 0); if (connection == null) { // exhausted all retries or timed out - give up Bid.Trace(" %d#, GetConnection failed because a pool timeout occurred and all retries were exhausted.\n", ObjectID); throw ADP.PooledOpenTimeout(); } return connection; } private DbConnectionPool GetConnectionPool(DbConnection owningObject, DbConnectionPoolGroup connectionPoolGroup) { // if poolgroup is disabled, it will be replaced with a new entry Debug.Assert(null != owningObject, "null owningObject?"); Debug.Assert(null != connectionPoolGroup, "null connectionPoolGroup?"); // It is possible that while the outer connection object has // been sitting around in a closed and unused state in some long // running app, the pruner may have come along and remove this // the pool entry from the master list. If we were to use a // pool entry in this state, we would create "unmanaged" pools, // which would be bad. To avoid this problem, we automagically // re-create the pool entry whenever it's disabled. // however, don't rebuild connectionOptions if no pooling is involved - let new connections do that work if (connectionPoolGroup.IsDisabled && (null != connectionPoolGroup.PoolGroupOptions)) { Bid.Trace(" %d#, DisabledPoolGroup=%d#\n", ObjectID, connectionPoolGroup.ObjectID); // reusing existing pool option in case user originally used SetConnectionPoolOptions DbConnectionPoolGroupOptions poolOptions = connectionPoolGroup.PoolGroupOptions; // get the string to hash on again DbConnectionOptions connectionOptions = connectionPoolGroup.ConnectionOptions; string connectionString = connectionOptions.UsersConnectionString(false); Debug.Assert(null != connectionOptions, "prevent expansion of connectionString"); connectionPoolGroup = GetConnectionPoolGroup(connectionString, poolOptions, ref connectionOptions); Debug.Assert(null != connectionPoolGroup, "null connectionPoolGroup?"); SetConnectionPoolGroup(owningObject, connectionPoolGroup); } DbConnectionPool connectionPool = connectionPoolGroup.GetConnectionPool(this); return connectionPool; } internal DbConnectionPoolGroup GetConnectionPoolGroup(string connectionString, DbConnectionPoolGroupOptions poolOptions, ref DbConnectionOptions userConnectionOptions) { if (ADP.IsEmpty(connectionString)) { return (DbConnectionPoolGroup)null; } DbConnectionPoolGroup connectionPoolGroup; Dictionary connectionPoolGroups = _connectionPoolGroups; if (!connectionPoolGroups.TryGetValue(connectionString, out connectionPoolGroup) || (connectionPoolGroup.IsDisabled && (null != connectionPoolGroup.PoolGroupOptions))) { // If we can't find an entry for the connection string in // our collection of pool entries, then we need to create a // new pool entry and add it to our collection. DbConnectionOptions connectionOptions = CreateConnectionOptions(connectionString, userConnectionOptions); if (null == connectionOptions) { throw ADP.InternalConnectionError(ADP.ConnectionError.ConnectionOptionsMissing); } string expandedConnectionString = connectionString; if (null == userConnectionOptions) { // we only allow one expansion on the connection string userConnectionOptions = connectionOptions; expandedConnectionString = connectionOptions.Expand(); // if the expanded string is same instance (default implementation), the use the already created options if ((object)expandedConnectionString != (object)connectionString) { // return GetConnectionPoolGroup(expandedConnectionString, null, ref userConnectionOptions); } } // We don't support connection pooling on Win9x; it lacks too many of the APIs we require. if ((null == poolOptions) && ADP.IsWindowsNT) { if (null != connectionPoolGroup) { // reusing existing pool option in case user originally used SetConnectionPoolOptions poolOptions = connectionPoolGroup.PoolGroupOptions; } else { // Note: may return null for non-pooled connections poolOptions = CreateConnectionPoolGroupOptions(connectionOptions); } } DbConnectionPoolGroup newConnectionPoolGroup = new DbConnectionPoolGroup(connectionOptions, poolOptions); newConnectionPoolGroup.ProviderInfo = CreateConnectionPoolGroupProviderInfo(connectionOptions); lock (this) { connectionPoolGroups = _connectionPoolGroups; if (!connectionPoolGroups.TryGetValue(expandedConnectionString, out connectionPoolGroup)) { // build new dictionary with space for new connection string Dictionary newConnectionPoolGroups = new Dictionary (1+connectionPoolGroups.Count); foreach (KeyValuePair entry in connectionPoolGroups) { newConnectionPoolGroups.Add(entry.Key, entry.Value); } // lock prevents race condition with PruneConnectionPoolGroups newConnectionPoolGroups.Add(expandedConnectionString, newConnectionPoolGroup); PerformanceCounters.NumberOfActiveConnectionPoolGroups.Increment(); connectionPoolGroup = newConnectionPoolGroup; _connectionPoolGroups = newConnectionPoolGroups; } else { Debug.Assert(!connectionPoolGroup.IsDisabled, "Disabled pool entry discovered"); } } Debug.Assert(null != connectionPoolGroup, "how did we not create a pool entry?"); Debug.Assert(null != userConnectionOptions, "how did we not have user connection options?"); } else if (null == userConnectionOptions) { userConnectionOptions = connectionPoolGroup.ConnectionOptions; } return connectionPoolGroup; } internal DbMetaDataFactory GetMetaDataFactory(DbConnectionPoolGroup connectionPoolGroup,DbConnectionInternal internalConnection){ Debug.Assert (connectionPoolGroup != null, "connectionPoolGroup may not be null."); // get the matadatafactory from the pool entry. If it does not already have one // create one and save it on the pool entry DbMetaDataFactory metaDataFactory = connectionPoolGroup.MetaDataFactory; // consider serializing this so we don't construct multiple metadata factories // if two threads happen to hit this at the same time. One will be GC'd if (metaDataFactory == null){ bool allowCache = false; metaDataFactory = CreateMetaDataFactory(internalConnection, out allowCache); if (allowCache) { connectionPoolGroup.MetaDataFactory = metaDataFactory; } } return metaDataFactory; } private void PruneConnectionPoolGroups(object state) { // when debugging this method, expect multiple threads at the same time if (Bid.AdvancedOn) { Bid.Trace(" %d#\n", ObjectID); } // First, walk the pool release list and attempt to clear each // pool, when the pool is finally empty, we dispose of it. If the // pool isn't empty, it's because there are active connections or // distributed transactions that need it. lock (_poolsToRelease) { if (0 != _poolsToRelease.Count) { DbConnectionPool[] poolsToRelease = _poolsToRelease.ToArray(); foreach (DbConnectionPool pool in poolsToRelease) { if (null != pool) { pool.Clear(); if (0 == pool.Count) { _poolsToRelease.Remove(pool); if (Bid.AdvancedOn) { Bid.Trace(" %d#, ReleasePool=%d#\n", ObjectID, pool.ObjectID); } PerformanceCounters.NumberOfInactiveConnectionPools.Decrement(); } } } } } // Next, walk the pool entry release list and dispose of each // pool entry when it is finally empty. If the pool entry isn't // empty, it's because there are active pools that need it. lock (_poolGroupsToRelease) { if (0 != _poolGroupsToRelease.Count) { DbConnectionPoolGroup[] poolGroupsToRelease = _poolGroupsToRelease.ToArray(); foreach (DbConnectionPoolGroup poolGroup in poolGroupsToRelease) { if (null != poolGroup) { poolGroup.Clear(); // may add entries to _poolsToRelease if (0 == poolGroup.Count) { _poolGroupsToRelease.Remove(poolGroup); if (Bid.AdvancedOn) { Bid.Trace(" %d#, ReleasePoolGroup=%d#\n", ObjectID, poolGroup.ObjectID); } PerformanceCounters.NumberOfInactiveConnectionPoolGroups.Decrement(); } } } } } // Finally, we walk through the collection of connection pool entries // and prune each one. This will cause any empty pools to be put // into the release list. lock (this) { Dictionary connectionPoolGroups = _connectionPoolGroups; Dictionary newConnectionPoolGroups = new Dictionary (connectionPoolGroups.Count); foreach (KeyValuePair entry in connectionPoolGroups) { if (null != entry.Value) { Debug.Assert(!entry.Value.IsDisabled, "Disabled pool entry discovered"); // entries start active and go idle during prune if all pools are gone // move idle entries from last prune pass to a queue for pending release // otherwise process entry which may move it from active to idle if (entry.Value.Prune()) { // may add entries to _poolsToRelease PerformanceCounters.NumberOfActiveConnectionPoolGroups.Decrement(); QueuePoolGroupForRelease(entry.Value); } else { newConnectionPoolGroups.Add(entry.Key, entry.Value); } } } _connectionPoolGroups = newConnectionPoolGroups; } } internal void QueuePoolForRelease(DbConnectionPool pool, bool clearing) { // Queue the pool up for release -- we'll clear it out and dispose // of it as the last part of the pruning timer callback so we don't // do it with the pool entry or the pool collection locked. Debug.Assert (null != pool, "null pool?"); // set the pool to the shutdown state to force all active // connections to be automatically disposed when they // are returned to the pool pool.Shutdown(); lock (_poolsToRelease) { if (clearing) { pool.Clear(); } _poolsToRelease.Add(pool); } PerformanceCounters.NumberOfInactiveConnectionPools.Increment(); } internal void QueuePoolGroupForRelease(DbConnectionPoolGroup poolGroup) { Debug.Assert (null != poolGroup, "null poolGroup?"); Bid.Trace(" %d#, poolGroup=%d#\n", ObjectID, poolGroup.ObjectID); lock (_poolGroupsToRelease) { _poolGroupsToRelease.Add(poolGroup); } PerformanceCounters.NumberOfInactiveConnectionPoolGroups.Increment(); } abstract protected DbConnectionInternal CreateConnection(DbConnectionOptions options, object poolGroupProviderInfo, DbConnectionPool pool, DbConnection owningConnection); abstract protected DbConnectionOptions CreateConnectionOptions(string connectionString, DbConnectionOptions previous); abstract protected DbConnectionPoolGroupOptions CreateConnectionPoolGroupOptions(DbConnectionOptions options); abstract internal DbConnectionPoolGroup GetConnectionPoolGroup(DbConnection connection); abstract internal DbConnectionInternal GetInnerConnection(DbConnection connection); abstract protected int GetObjectId(DbConnection connection); abstract internal void PermissionDemand(DbConnection outerConnection); abstract internal void SetConnectionPoolGroup(DbConnection outerConnection, DbConnectionPoolGroup poolGroup); abstract internal void SetInnerConnectionEvent(DbConnection owningObject, DbConnectionInternal to); abstract internal bool SetInnerConnectionFrom(DbConnection owningObject, DbConnectionInternal to, DbConnectionInternal from) ; abstract internal void SetInnerConnectionTo(DbConnection owningObject, DbConnectionInternal to); } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. //------------------------------------------------------------------------------ // // Copyright (c) Microsoft Corporation. All rights reserved. // //[....] //[....] //----------------------------------------------------------------------------- namespace System.Data.ProviderBase { using System; using System.Collections.Generic; using System.Diagnostics; using System.Data.Common; using System.Threading; #if ORACLE using System.Data.OracleClient; #endif internal abstract class DbConnectionFactory { private Dictionary_connectionPoolGroups; private readonly List _poolsToRelease; private readonly List _poolGroupsToRelease; private readonly DbConnectionPoolCounters _performanceCounters; private readonly Timer _pruningTimer; private const int PruningDueTime =4*60*1000; // 4 minutes private const int PruningPeriod = 30*1000; // thirty seconds private static int _objectTypeCount; // Bid counter internal readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); protected DbConnectionFactory() : this (DbConnectionPoolCountersNoCounters.SingletonInstance) { } protected DbConnectionFactory(DbConnectionPoolCounters performanceCounters) { _performanceCounters = performanceCounters; _connectionPoolGroups = new Dictionary (); _poolsToRelease = new List (); _poolGroupsToRelease = new List (); _pruningTimer = CreatePruningTimer(); } internal DbConnectionPoolCounters PerformanceCounters { get { return _performanceCounters; } } abstract public DbProviderFactory ProviderFactory { get; } internal int ObjectID { get { return _objectID; } } public void ClearAllPools() { IntPtr hscp; Bid.ScopeEnter(out hscp, " "); try { Dictionary connectionPoolGroups = _connectionPoolGroups; foreach (KeyValuePair entry in connectionPoolGroups) { DbConnectionPoolGroup poolGroup = entry.Value; if (null != poolGroup) { Debug.Assert(!poolGroup.IsDisabled, "Disabled pool entry discovered"); poolGroup.Clear(); } } } finally { Bid.ScopeLeave(ref hscp); } } public void ClearPool(DbConnection connection) { ADP.CheckArgumentNull(connection, "connection"); IntPtr hscp; Bid.ScopeEnter(out hscp, " %d#" , GetObjectId(connection)); try { DbConnectionPoolGroup poolGroup = GetConnectionPoolGroup(connection); if (null != poolGroup) { poolGroup.Clear(); } } finally { Bid.ScopeLeave(ref hscp); } } #if !ORACLE public void ClearPool(string connectionString) { ADP.CheckArgumentNull(connectionString, "connectionString"); IntPtr hscp; Bid.ScopeEnter(out hscp, " connectionString"); try { DbConnectionPoolGroup poolGroup; Dictionary connectionPoolGroups = _connectionPoolGroups; if (connectionPoolGroups.TryGetValue(connectionString, out poolGroup)) { poolGroup.Clear(); } } finally { Bid.ScopeLeave(ref hscp); } } #endif internal virtual DbConnectionPoolProviderInfo CreateConnectionPoolProviderInfo(DbConnectionOptions connectionOptions){ return null; } virtual protected DbMetaDataFactory CreateMetaDataFactory(DbConnectionInternal internalConnection, out bool cacheMetaDataFactory) { // providers that support GetSchema must override this with a method that creates a meta data // factory appropriate for them. cacheMetaDataFactory = false; throw ADP.NotSupported(); } internal DbConnectionInternal CreateNonPooledConnection(DbConnection owningConnection, DbConnectionPoolGroup poolGroup) { Debug.Assert(null != owningConnection, "null owningConnection?"); Debug.Assert(null != poolGroup, "null poolGroup?"); DbConnectionOptions connectionOptions = poolGroup.ConnectionOptions; DbConnectionPoolGroupProviderInfo poolGroupProviderInfo = poolGroup.ProviderInfo; DbConnectionInternal newConnection = CreateConnection(connectionOptions, poolGroupProviderInfo, null, owningConnection); if (null != newConnection) { PerformanceCounters.HardConnectsPerSecond.Increment(); newConnection.MakeNonPooledObject(owningConnection, PerformanceCounters); } Bid.Trace(" %d#, Non-pooled database connection created.\n", ObjectID); return newConnection; } internal DbConnectionInternal CreatePooledConnection(DbConnection owningConnection, DbConnectionPool pool, DbConnectionOptions options) { Debug.Assert(null != pool, "null pool?"); DbConnectionPoolGroupProviderInfo poolGroupProviderInfo = pool.PoolGroup.ProviderInfo; DbConnectionInternal newConnection = CreateConnection(options, poolGroupProviderInfo, pool, owningConnection); if (null != newConnection) { PerformanceCounters.HardConnectsPerSecond.Increment(); newConnection.MakePooledConnection(pool); } Bid.Trace(" %d#, Pooled database connection created.\n", ObjectID); return newConnection; } virtual internal DbConnectionPoolGroupProviderInfo CreateConnectionPoolGroupProviderInfo (DbConnectionOptions connectionOptions) { return null; } private Timer CreatePruningTimer() { TimerCallback callback = new TimerCallback(PruneConnectionPoolGroups); return new Timer(callback, null, PruningDueTime, PruningPeriod); } #if !ORACLE protected DbConnectionOptions FindConnectionOptions(string connectionString) { if (!ADP.IsEmpty(connectionString)) { DbConnectionPoolGroup connectionPoolGroup; Dictionary connectionPoolGroups = _connectionPoolGroups; if (connectionPoolGroups.TryGetValue(connectionString, out connectionPoolGroup)) { return connectionPoolGroup.ConnectionOptions; } } return null; } #endif internal DbConnectionInternal GetConnection(DbConnection owningConnection) { Debug.Assert(null != owningConnection, "null owningConnection?"); DbConnectionPoolGroup poolGroup; DbConnectionPool connectionPool; DbConnectionInternal connection; // SQLBU 431251: // Work around race condition with clearing the pool between GetConnectionPool obtaining pool // and GetConnection on the pool checking the pool state. Clearing the pool in this window // will switch the pool into the ShuttingDown state, and GetConnection will return null. // There is probably a better solution involving locking the pool/group, but that entails a major // re-design of the connection pooling synchronization, so is post-poned for now. // VSDD 674236: use retriesLeft to prevent CPU spikes with incremental sleep // start with one msec, double the time every retry // max time is: 1 + 2 + 4 + ... + 2^(retries-1) == 2^retries -1 == 1023ms (for 10 retries) int retriesLeft = 10; int timeBetweenRetriesMilliseconds = 1; do { poolGroup = GetConnectionPoolGroup(owningConnection); connectionPool = GetConnectionPool(owningConnection, poolGroup); if (null == connectionPool) { // If GetConnectionPool returns null, we can be certain that // this connection should not be pooled via DbConnectionPool // or have a disabled pool entry. poolGroup = GetConnectionPoolGroup(owningConnection); // previous entry have been disabled connection = CreateNonPooledConnection(owningConnection, poolGroup); PerformanceCounters.NumberOfNonPooledConnections.Increment(); } else { connection = connectionPool.GetConnection(owningConnection); if (connection == null) { // connection creation failed on semaphore waiting or if max pool reached if (connectionPool.IsRunning) { // If GetConnection failed while the pool is running, the pool timeout occurred. Bid.Trace(" %d#, GetConnection failed because a pool timeout occurred.\n", ObjectID); throw ADP.PooledOpenTimeout(); } else { // We've hit the race condition, where the pool was shut down after we got it from the group. // Yield time slice to allow shut down activities to complete and a new, running pool to be instantiated // before retrying. Threading.Thread.Sleep(timeBetweenRetriesMilliseconds); timeBetweenRetriesMilliseconds *= 2; // double the wait time for next iteration } } } } while (connection == null && retriesLeft-- > 0); if (connection == null) { // exhausted all retries or timed out - give up Bid.Trace(" %d#, GetConnection failed because a pool timeout occurred and all retries were exhausted.\n", ObjectID); throw ADP.PooledOpenTimeout(); } return connection; } private DbConnectionPool GetConnectionPool(DbConnection owningObject, DbConnectionPoolGroup connectionPoolGroup) { // if poolgroup is disabled, it will be replaced with a new entry Debug.Assert(null != owningObject, "null owningObject?"); Debug.Assert(null != connectionPoolGroup, "null connectionPoolGroup?"); // It is possible that while the outer connection object has // been sitting around in a closed and unused state in some long // running app, the pruner may have come along and remove this // the pool entry from the master list. If we were to use a // pool entry in this state, we would create "unmanaged" pools, // which would be bad. To avoid this problem, we automagically // re-create the pool entry whenever it's disabled. // however, don't rebuild connectionOptions if no pooling is involved - let new connections do that work if (connectionPoolGroup.IsDisabled && (null != connectionPoolGroup.PoolGroupOptions)) { Bid.Trace(" %d#, DisabledPoolGroup=%d#\n", ObjectID, connectionPoolGroup.ObjectID); // reusing existing pool option in case user originally used SetConnectionPoolOptions DbConnectionPoolGroupOptions poolOptions = connectionPoolGroup.PoolGroupOptions; // get the string to hash on again DbConnectionOptions connectionOptions = connectionPoolGroup.ConnectionOptions; string connectionString = connectionOptions.UsersConnectionString(false); Debug.Assert(null != connectionOptions, "prevent expansion of connectionString"); connectionPoolGroup = GetConnectionPoolGroup(connectionString, poolOptions, ref connectionOptions); Debug.Assert(null != connectionPoolGroup, "null connectionPoolGroup?"); SetConnectionPoolGroup(owningObject, connectionPoolGroup); } DbConnectionPool connectionPool = connectionPoolGroup.GetConnectionPool(this); return connectionPool; } internal DbConnectionPoolGroup GetConnectionPoolGroup(string connectionString, DbConnectionPoolGroupOptions poolOptions, ref DbConnectionOptions userConnectionOptions) { if (ADP.IsEmpty(connectionString)) { return (DbConnectionPoolGroup)null; } DbConnectionPoolGroup connectionPoolGroup; Dictionary connectionPoolGroups = _connectionPoolGroups; if (!connectionPoolGroups.TryGetValue(connectionString, out connectionPoolGroup) || (connectionPoolGroup.IsDisabled && (null != connectionPoolGroup.PoolGroupOptions))) { // If we can't find an entry for the connection string in // our collection of pool entries, then we need to create a // new pool entry and add it to our collection. DbConnectionOptions connectionOptions = CreateConnectionOptions(connectionString, userConnectionOptions); if (null == connectionOptions) { throw ADP.InternalConnectionError(ADP.ConnectionError.ConnectionOptionsMissing); } string expandedConnectionString = connectionString; if (null == userConnectionOptions) { // we only allow one expansion on the connection string userConnectionOptions = connectionOptions; expandedConnectionString = connectionOptions.Expand(); // if the expanded string is same instance (default implementation), the use the already created options if ((object)expandedConnectionString != (object)connectionString) { // return GetConnectionPoolGroup(expandedConnectionString, null, ref userConnectionOptions); } } // We don't support connection pooling on Win9x; it lacks too many of the APIs we require. if ((null == poolOptions) && ADP.IsWindowsNT) { if (null != connectionPoolGroup) { // reusing existing pool option in case user originally used SetConnectionPoolOptions poolOptions = connectionPoolGroup.PoolGroupOptions; } else { // Note: may return null for non-pooled connections poolOptions = CreateConnectionPoolGroupOptions(connectionOptions); } } DbConnectionPoolGroup newConnectionPoolGroup = new DbConnectionPoolGroup(connectionOptions, poolOptions); newConnectionPoolGroup.ProviderInfo = CreateConnectionPoolGroupProviderInfo(connectionOptions); lock (this) { connectionPoolGroups = _connectionPoolGroups; if (!connectionPoolGroups.TryGetValue(expandedConnectionString, out connectionPoolGroup)) { // build new dictionary with space for new connection string Dictionary newConnectionPoolGroups = new Dictionary (1+connectionPoolGroups.Count); foreach (KeyValuePair entry in connectionPoolGroups) { newConnectionPoolGroups.Add(entry.Key, entry.Value); } // lock prevents race condition with PruneConnectionPoolGroups newConnectionPoolGroups.Add(expandedConnectionString, newConnectionPoolGroup); PerformanceCounters.NumberOfActiveConnectionPoolGroups.Increment(); connectionPoolGroup = newConnectionPoolGroup; _connectionPoolGroups = newConnectionPoolGroups; } else { Debug.Assert(!connectionPoolGroup.IsDisabled, "Disabled pool entry discovered"); } } Debug.Assert(null != connectionPoolGroup, "how did we not create a pool entry?"); Debug.Assert(null != userConnectionOptions, "how did we not have user connection options?"); } else if (null == userConnectionOptions) { userConnectionOptions = connectionPoolGroup.ConnectionOptions; } return connectionPoolGroup; } internal DbMetaDataFactory GetMetaDataFactory(DbConnectionPoolGroup connectionPoolGroup,DbConnectionInternal internalConnection){ Debug.Assert (connectionPoolGroup != null, "connectionPoolGroup may not be null."); // get the matadatafactory from the pool entry. If it does not already have one // create one and save it on the pool entry DbMetaDataFactory metaDataFactory = connectionPoolGroup.MetaDataFactory; // consider serializing this so we don't construct multiple metadata factories // if two threads happen to hit this at the same time. One will be GC'd if (metaDataFactory == null){ bool allowCache = false; metaDataFactory = CreateMetaDataFactory(internalConnection, out allowCache); if (allowCache) { connectionPoolGroup.MetaDataFactory = metaDataFactory; } } return metaDataFactory; } private void PruneConnectionPoolGroups(object state) { // when debugging this method, expect multiple threads at the same time if (Bid.AdvancedOn) { Bid.Trace(" %d#\n", ObjectID); } // First, walk the pool release list and attempt to clear each // pool, when the pool is finally empty, we dispose of it. If the // pool isn't empty, it's because there are active connections or // distributed transactions that need it. lock (_poolsToRelease) { if (0 != _poolsToRelease.Count) { DbConnectionPool[] poolsToRelease = _poolsToRelease.ToArray(); foreach (DbConnectionPool pool in poolsToRelease) { if (null != pool) { pool.Clear(); if (0 == pool.Count) { _poolsToRelease.Remove(pool); if (Bid.AdvancedOn) { Bid.Trace(" %d#, ReleasePool=%d#\n", ObjectID, pool.ObjectID); } PerformanceCounters.NumberOfInactiveConnectionPools.Decrement(); } } } } } // Next, walk the pool entry release list and dispose of each // pool entry when it is finally empty. If the pool entry isn't // empty, it's because there are active pools that need it. lock (_poolGroupsToRelease) { if (0 != _poolGroupsToRelease.Count) { DbConnectionPoolGroup[] poolGroupsToRelease = _poolGroupsToRelease.ToArray(); foreach (DbConnectionPoolGroup poolGroup in poolGroupsToRelease) { if (null != poolGroup) { poolGroup.Clear(); // may add entries to _poolsToRelease if (0 == poolGroup.Count) { _poolGroupsToRelease.Remove(poolGroup); if (Bid.AdvancedOn) { Bid.Trace(" %d#, ReleasePoolGroup=%d#\n", ObjectID, poolGroup.ObjectID); } PerformanceCounters.NumberOfInactiveConnectionPoolGroups.Decrement(); } } } } } // Finally, we walk through the collection of connection pool entries // and prune each one. This will cause any empty pools to be put // into the release list. lock (this) { Dictionary connectionPoolGroups = _connectionPoolGroups; Dictionary newConnectionPoolGroups = new Dictionary (connectionPoolGroups.Count); foreach (KeyValuePair entry in connectionPoolGroups) { if (null != entry.Value) { Debug.Assert(!entry.Value.IsDisabled, "Disabled pool entry discovered"); // entries start active and go idle during prune if all pools are gone // move idle entries from last prune pass to a queue for pending release // otherwise process entry which may move it from active to idle if (entry.Value.Prune()) { // may add entries to _poolsToRelease PerformanceCounters.NumberOfActiveConnectionPoolGroups.Decrement(); QueuePoolGroupForRelease(entry.Value); } else { newConnectionPoolGroups.Add(entry.Key, entry.Value); } } } _connectionPoolGroups = newConnectionPoolGroups; } } internal void QueuePoolForRelease(DbConnectionPool pool, bool clearing) { // Queue the pool up for release -- we'll clear it out and dispose // of it as the last part of the pruning timer callback so we don't // do it with the pool entry or the pool collection locked. Debug.Assert (null != pool, "null pool?"); // set the pool to the shutdown state to force all active // connections to be automatically disposed when they // are returned to the pool pool.Shutdown(); lock (_poolsToRelease) { if (clearing) { pool.Clear(); } _poolsToRelease.Add(pool); } PerformanceCounters.NumberOfInactiveConnectionPools.Increment(); } internal void QueuePoolGroupForRelease(DbConnectionPoolGroup poolGroup) { Debug.Assert (null != poolGroup, "null poolGroup?"); Bid.Trace(" %d#, poolGroup=%d#\n", ObjectID, poolGroup.ObjectID); lock (_poolGroupsToRelease) { _poolGroupsToRelease.Add(poolGroup); } PerformanceCounters.NumberOfInactiveConnectionPoolGroups.Increment(); } abstract protected DbConnectionInternal CreateConnection(DbConnectionOptions options, object poolGroupProviderInfo, DbConnectionPool pool, DbConnection owningConnection); abstract protected DbConnectionOptions CreateConnectionOptions(string connectionString, DbConnectionOptions previous); abstract protected DbConnectionPoolGroupOptions CreateConnectionPoolGroupOptions(DbConnectionOptions options); abstract internal DbConnectionPoolGroup GetConnectionPoolGroup(DbConnection connection); abstract internal DbConnectionInternal GetInnerConnection(DbConnection connection); abstract protected int GetObjectId(DbConnection connection); abstract internal void PermissionDemand(DbConnection outerConnection); abstract internal void SetConnectionPoolGroup(DbConnection outerConnection, DbConnectionPoolGroup poolGroup); abstract internal void SetInnerConnectionEvent(DbConnection owningObject, DbConnectionInternal to); abstract internal bool SetInnerConnectionFrom(DbConnection owningObject, DbConnectionInternal to, DbConnectionInternal from) ; abstract internal void SetInnerConnectionTo(DbConnection owningObject, DbConnectionInternal to); } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- CodeDefaultValueExpression.cs
- MediaContextNotificationWindow.cs
- LocatorPart.cs
- LicenseProviderAttribute.cs
- COM2IVsPerPropertyBrowsingHandler.cs
- SmtpMail.cs
- DefaultValueTypeConverter.cs
- QuaternionAnimation.cs
- EntityDescriptor.cs
- Brush.cs
- DBSqlParser.cs
- AggregateNode.cs
- StrokeCollectionConverter.cs
- Speller.cs
- WindowsMenu.cs
- JsonFaultDetail.cs
- DbParameterCollectionHelper.cs
- CustomErrorsSection.cs
- DesignerDataRelationship.cs
- DataGridViewBindingCompleteEventArgs.cs
- DetailsViewUpdateEventArgs.cs
- DataGridViewAutoSizeModeEventArgs.cs
- EdmSchemaError.cs
- AsymmetricKeyExchangeFormatter.cs
- BinHexEncoder.cs
- DesignerRegion.cs
- UriWriter.cs
- COM2ColorConverter.cs
- AppDomainFactory.cs
- XamlStyleSerializer.cs
- LogAppendAsyncResult.cs
- ListViewSelectEventArgs.cs
- GcSettings.cs
- EncryptedKey.cs
- WebPartDeleteVerb.cs
- SHA384Managed.cs
- GifBitmapDecoder.cs
- EventQueueState.cs
- ToolStripRenderer.cs
- TransformPatternIdentifiers.cs
- CatalogZoneBase.cs
- SingleSelectRootGridEntry.cs
- SqlVisitor.cs
- CngProperty.cs
- sortedlist.cs
- QuotedPrintableStream.cs
- EarlyBoundInfo.cs
- MaterialCollection.cs
- TabControlCancelEvent.cs
- DBNull.cs
- IfJoinedCondition.cs
- InProcStateClientManager.cs
- OracleDateTime.cs
- XmlElementAttributes.cs
- ReadWriteSpinLock.cs
- StateChangeEvent.cs
- KeyNotFoundException.cs
- InputMethodStateTypeInfo.cs
- ArrayConverter.cs
- SqlUDTStorage.cs
- RelatedCurrencyManager.cs
- PointLight.cs
- ParagraphVisual.cs
- DataTableClearEvent.cs
- DefaultMemberAttribute.cs
- ApplicationTrust.cs
- RuntimeResourceSet.cs
- HtmlInputReset.cs
- GridLengthConverter.cs
- GPPOINTF.cs
- TemplatedAdorner.cs
- LinkUtilities.cs
- MachineSettingsSection.cs
- TextAction.cs
- InstanceLockLostException.cs
- HeaderUtility.cs
- MasterPageCodeDomTreeGenerator.cs
- panel.cs
- HtmlTableRowCollection.cs
- ModuleConfigurationInfo.cs
- StructuralObject.cs
- OutputCacheProfile.cs
- SqlCaseSimplifier.cs
- IListConverters.cs
- DocumentSequenceHighlightLayer.cs
- DataRelation.cs
- DCSafeHandle.cs
- SingleStorage.cs
- RtfFormatStack.cs
- ValidatingReaderNodeData.cs
- GACMembershipCondition.cs
- Line.cs
- ProfileInfo.cs
- BinHexDecoder.cs
- XPathArrayIterator.cs
- XMLUtil.cs
- ScriptIgnoreAttribute.cs
- ServiceHostingEnvironmentSection.cs
- AxHostDesigner.cs
- FacetValues.cs