| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618 |
- //------------------------------------------------------------------------------
- // <copyright file="DbConnectionFactory.cs" company="Microsoft">
- // Copyright (c) Microsoft Corporation. All rights reserved.
- // </copyright>
- // <owner current="true" primary="true">[....]</owner>
- // <owner current="true" primary="false">[....]</owner>
- //------------------------------------------------------------------------------
- namespace System.Data.ProviderBase {
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.Data.Common;
- using System.Linq;
- using System.Threading;
- using System.Threading.Tasks;
- internal abstract class DbConnectionFactory {
- private Dictionary<DbConnectionPoolKey, DbConnectionPoolGroup> _connectionPoolGroups;
- private readonly List<DbConnectionPool> _poolsToRelease;
- private readonly List<DbConnectionPoolGroup> _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);
- // s_pendingOpenNonPooled is an array of tasks used to throttle creation of non-pooled connections to
- // a maximum of Environment.ProcessorCount at a time.
- static int s_pendingOpenNonPooledNext = 0;
- static Task<DbConnectionInternal>[] s_pendingOpenNonPooled = new Task<DbConnectionInternal>[Environment.ProcessorCount];
- static Task<DbConnectionInternal> s_completedTask;
- protected DbConnectionFactory() : this (DbConnectionPoolCountersNoCounters.SingletonInstance) { }
- protected DbConnectionFactory(DbConnectionPoolCounters performanceCounters) {
- _performanceCounters = performanceCounters;
- _connectionPoolGroups = new Dictionary<DbConnectionPoolKey, DbConnectionPoolGroup>();
- _poolsToRelease = new List<DbConnectionPool>();
- _poolGroupsToRelease = new List<DbConnectionPoolGroup>();
- _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, "<prov.DbConnectionFactory.ClearAllPools|API> ");
- try {
- Dictionary<DbConnectionPoolKey, DbConnectionPoolGroup> connectionPoolGroups = _connectionPoolGroups;
- foreach (KeyValuePair<DbConnectionPoolKey, DbConnectionPoolGroup> entry in connectionPoolGroups) {
- DbConnectionPoolGroup poolGroup = entry.Value;
- if (null != poolGroup) {
- poolGroup.Clear();
- }
- }
- }
- finally {
- Bid.ScopeLeave(ref hscp);
- }
- }
- public void ClearPool(DbConnection connection) {
- ADP.CheckArgumentNull(connection, "connection");
- IntPtr hscp;
- Bid.ScopeEnter(out hscp, "<prov.DbConnectionFactory.ClearPool|API> %d#" , GetObjectId(connection));
- try {
- DbConnectionPoolGroup poolGroup = GetConnectionPoolGroup(connection);
- if (null != poolGroup) {
- poolGroup.Clear();
- }
- }
- finally {
- Bid.ScopeLeave(ref hscp);
- }
- }
- public void ClearPool(DbConnectionPoolKey key) {
- Debug.Assert(key != null, "key cannot be null");
- ADP.CheckArgumentNull(key.ConnectionString, "key.ConnectionString");
- IntPtr hscp;
- Bid.ScopeEnter(out hscp, "<prov.DbConnectionFactory.ClearPool|API> connectionString");
- try {
- DbConnectionPoolGroup poolGroup;
- Dictionary<DbConnectionPoolKey, DbConnectionPoolGroup> connectionPoolGroups = _connectionPoolGroups;
- if (connectionPoolGroups.TryGetValue(key, out poolGroup)) {
- poolGroup.Clear();
- }
- }
- finally {
- Bid.ScopeLeave(ref hscp);
- }
- }
- 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, DbConnectionOptions userOptions) {
- Debug.Assert(null != owningConnection, "null owningConnection?");
- Debug.Assert(null != poolGroup, "null poolGroup?");
- DbConnectionOptions connectionOptions = poolGroup.ConnectionOptions;
- DbConnectionPoolGroupProviderInfo poolGroupProviderInfo = poolGroup.ProviderInfo;
- DbConnectionPoolKey poolKey = poolGroup.PoolKey;
- DbConnectionInternal newConnection = CreateConnection(connectionOptions, poolKey, poolGroupProviderInfo, null, owningConnection, userOptions);
- if (null != newConnection) {
- #if !MOBILE
- PerformanceCounters.HardConnectsPerSecond.Increment();
- #endif
- newConnection.MakeNonPooledObject(owningConnection, PerformanceCounters);
- }
- Bid.Trace("<prov.DbConnectionFactory.CreateNonPooledConnection|RES|CPOOL> %d#, Non-pooled database connection created.\n", ObjectID);
- return newConnection;
- }
- internal DbConnectionInternal CreatePooledConnection(DbConnectionPool pool, DbConnection owningObject, DbConnectionOptions options, DbConnectionPoolKey poolKey, DbConnectionOptions userOptions) {
- Debug.Assert(null != pool, "null pool?");
- DbConnectionPoolGroupProviderInfo poolGroupProviderInfo = pool.PoolGroup.ProviderInfo;
- DbConnectionInternal newConnection = CreateConnection(options, poolKey, poolGroupProviderInfo, pool, owningObject, userOptions);
- if (null != newConnection) {
- #if !MOBILE
- PerformanceCounters.HardConnectsPerSecond.Increment();
- #endif
- newConnection.MakePooledConnection(pool);
- }
- Bid.Trace("<prov.DbConnectionFactory.CreatePooledConnection|RES|CPOOL> %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);
- }
- protected DbConnectionOptions FindConnectionOptions(DbConnectionPoolKey key) {
- Debug.Assert(key != null, "key cannot be null");
- if (!ADP.IsEmpty(key.ConnectionString)) {
- DbConnectionPoolGroup connectionPoolGroup;
- Dictionary<DbConnectionPoolKey, DbConnectionPoolGroup> connectionPoolGroups = _connectionPoolGroups;
- if (connectionPoolGroups.TryGetValue(key, out connectionPoolGroup)) {
- return connectionPoolGroup.ConnectionOptions;
- }
- }
- return null;
- }
- // GetCompletedTask must be called from within s_pendingOpenPooled lock
- static Task<DbConnectionInternal> GetCompletedTask()
- {
- if (s_completedTask == null) {
- TaskCompletionSource<DbConnectionInternal> source = new TaskCompletionSource<DbConnectionInternal>();
- source.SetResult(null);
- s_completedTask = source.Task;
- }
- return s_completedTask;
- }
- internal bool TryGetConnection(DbConnection owningConnection, TaskCompletionSource<DbConnectionInternal> retry, DbConnectionOptions userOptions, DbConnectionInternal oldConnection, out DbConnectionInternal connection) {
- Debug.Assert(null != owningConnection, "null owningConnection?");
- DbConnectionPoolGroup poolGroup;
- DbConnectionPool connectionPool;
- connection = null;
- // 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);
- // Doing this on the callers thread is important because it looks up the WindowsIdentity from the thread.
- 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
- if (retry != null) {
- Task<DbConnectionInternal> newTask;
- CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
- lock (s_pendingOpenNonPooled) {
- // look for an available task slot (completed or empty)
- int idx;
- for (idx=0; idx<s_pendingOpenNonPooled.Length; idx++) {
- Task task = s_pendingOpenNonPooled[idx];
- if (task == null) {
- s_pendingOpenNonPooled[idx] = GetCompletedTask();
- break;
- }
- else if (task.IsCompleted) {
- break;
- }
- }
- // if didn't find one, pick the next one in round-robbin fashion
- if (idx == s_pendingOpenNonPooled.Length) {
- idx = s_pendingOpenNonPooledNext++ % s_pendingOpenNonPooled.Length;
- }
- // now that we have an antecedent task, schedule our work when it is completed.
- // If it is a new slot or a compelted task, this continuation will start right away.
- //
- newTask = s_pendingOpenNonPooled[idx].ContinueWith((_) => {
- Transactions.Transaction originalTransaction = ADP.GetCurrentTransaction();
- try
- {
- ADP.SetCurrentTransaction(retry.Task.AsyncState as Transactions.Transaction);
- var newConnection = CreateNonPooledConnection(owningConnection, poolGroup, userOptions);
- if ((oldConnection != null) && (oldConnection.State == ConnectionState.Open)) {
- oldConnection.PrepareForReplaceConnection();
- oldConnection.Dispose();
- }
- return newConnection;
- } finally {
- ADP.SetCurrentTransaction(originalTransaction);
- }
- }, cancellationTokenSource.Token, TaskContinuationOptions.LongRunning, TaskScheduler.Default);
- // Place this new task in the slot so any future work will be queued behind it
- s_pendingOpenNonPooled[idx] = newTask;
- }
- // Set up the timeout (if needed)
- if (owningConnection.ConnectionTimeout > 0) {
- int connectionTimeoutMilliseconds = owningConnection.ConnectionTimeout * 1000;
- cancellationTokenSource.CancelAfter(connectionTimeoutMilliseconds);
- }
- // once the task is done, propagate the final results to the original caller
- newTask.ContinueWith((task) => {
- cancellationTokenSource.Dispose();
- if (task.IsCanceled) {
- retry.TrySetException(ADP.ExceptionWithStackTrace(ADP.NonPooledOpenTimeout()));
- } else if (task.IsFaulted) {
- retry.TrySetException(task.Exception.InnerException);
- }
- else {
- if (retry.TrySetResult(task.Result)) {
- #if !MOBILE
- PerformanceCounters.NumberOfNonPooledConnections.Increment();
- #endif
- }
- else {
- // The outer TaskCompletionSource was already completed
- // Which means that we don't know if someone has messed with the outer connection in the middle of creation
- // So the best thing to do now is to destroy the newly created connection
- task.Result.DoomThisConnection();
- task.Result.Dispose();
- }
- }
- }, TaskScheduler.Default);
- return false;
- }
- connection = CreateNonPooledConnection(owningConnection, poolGroup, userOptions);
- #if !MOBILE
- PerformanceCounters.NumberOfNonPooledConnections.Increment();
- #endif
- }
- else {
- if (owningConnection.ForceNewConnection) {
- Debug.Assert(!(oldConnection is DbConnectionClosed), "Force new connection, but there is no old connection");
- connection = connectionPool.ReplaceConnection(owningConnection, userOptions, oldConnection);
- }
- else {
- if (!connectionPool.TryGetConnection(owningConnection, retry, userOptions, out connection)) {
- return false;
- }
- }
- 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("<prov.DbConnectionFactory.GetConnection|RES|CPOOL> %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("<prov.DbConnectionFactory.GetConnection|RES|CPOOL> %d#, GetConnection failed because a pool timeout occurred and all retries were exhausted.\n", ObjectID);
- throw ADP.PooledOpenTimeout();
- }
- return true;
- }
- 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("<prov.DbConnectionFactory.GetConnectionPool|RES|INFO|CPOOL> %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;
- Debug.Assert(null != connectionOptions, "prevent expansion of connectionString");
- connectionPoolGroup = GetConnectionPoolGroup(connectionPoolGroup.PoolKey, poolOptions, ref connectionOptions);
- Debug.Assert(null != connectionPoolGroup, "null connectionPoolGroup?");
- SetConnectionPoolGroup(owningObject, connectionPoolGroup);
- }
- DbConnectionPool connectionPool = connectionPoolGroup.GetConnectionPool(this);
- return connectionPool;
- }
- internal DbConnectionPoolGroup GetConnectionPoolGroup(DbConnectionPoolKey key, DbConnectionPoolGroupOptions poolOptions, ref DbConnectionOptions userConnectionOptions) {
- if (ADP.IsEmpty(key.ConnectionString)) {
- return (DbConnectionPoolGroup)null;
- }
- DbConnectionPoolGroup connectionPoolGroup;
- Dictionary<DbConnectionPoolKey, DbConnectionPoolGroup> connectionPoolGroups = _connectionPoolGroups;
- if (!connectionPoolGroups.TryGetValue(key, 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(key.ConnectionString, userConnectionOptions);
- if (null == connectionOptions) {
- throw ADP.InternalConnectionError(ADP.ConnectionError.ConnectionOptionsMissing);
- }
- string expandedConnectionString = key.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)key.ConnectionString) {
- //
- DbConnectionPoolKey newKey = (DbConnectionPoolKey) ((ICloneable) key).Clone();
- newKey.ConnectionString = expandedConnectionString;
- return GetConnectionPoolGroup(newKey, 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, key, poolOptions);
- newConnectionPoolGroup.ProviderInfo = CreateConnectionPoolGroupProviderInfo(connectionOptions);
- lock (this) {
- connectionPoolGroups = _connectionPoolGroups;
- if (!connectionPoolGroups.TryGetValue(key, out connectionPoolGroup)) {
- // build new dictionary with space for new connection string
- Dictionary<DbConnectionPoolKey, DbConnectionPoolGroup> newConnectionPoolGroups = new Dictionary<DbConnectionPoolKey, DbConnectionPoolGroup>(1+connectionPoolGroups.Count);
- foreach (KeyValuePair<DbConnectionPoolKey, DbConnectionPoolGroup> entry in connectionPoolGroups) {
- newConnectionPoolGroups.Add(entry.Key, entry.Value);
- }
- // lock prevents race condition with PruneConnectionPoolGroups
- newConnectionPoolGroups.Add(key, newConnectionPoolGroup);
- #if !MOBILE
- PerformanceCounters.NumberOfActiveConnectionPoolGroups.Increment();
- #endif
- 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("<prov.DbConnectionFactory.PruneConnectionPoolGroups|RES|INFO|CPOOL> %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("<prov.DbConnectionFactory.PruneConnectionPoolGroups|RES|INFO|CPOOL> %d#, ReleasePool=%d#\n", ObjectID, pool.ObjectID);
- }
- #if !MOBILE
- PerformanceCounters.NumberOfInactiveConnectionPools.Decrement();
- #endif
- }
- }
- }
- }
- }
- // 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) {
- int poolsLeft = poolGroup.Clear(); // may add entries to _poolsToRelease
- if (0 == poolsLeft) {
- _poolGroupsToRelease.Remove(poolGroup);
- if (Bid.AdvancedOn) {
- Bid.Trace("<prov.DbConnectionFactory.PruneConnectionPoolGroups|RES|INFO|CPOOL> %d#, ReleasePoolGroup=%d#\n", ObjectID, poolGroup.ObjectID);
- }
- #if !MOBILE
- PerformanceCounters.NumberOfInactiveConnectionPoolGroups.Decrement();
- #endif
- }
- }
- }
- }
- }
- // 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<DbConnectionPoolKey, DbConnectionPoolGroup> connectionPoolGroups = _connectionPoolGroups;
- Dictionary<DbConnectionPoolKey, DbConnectionPoolGroup> newConnectionPoolGroups = new Dictionary<DbConnectionPoolKey, DbConnectionPoolGroup>(connectionPoolGroups.Count);
- foreach (KeyValuePair<DbConnectionPoolKey, DbConnectionPoolGroup> 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
- #if !MOBILE
- PerformanceCounters.NumberOfActiveConnectionPoolGroups.Decrement();
- #endif
- 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);
- }
- #if !MOBILE
- PerformanceCounters.NumberOfInactiveConnectionPools.Increment();
- #endif
- }
- internal void QueuePoolGroupForRelease(DbConnectionPoolGroup poolGroup) {
- Debug.Assert (null != poolGroup, "null poolGroup?");
- Bid.Trace("<prov.DbConnectionFactory.QueuePoolGroupForRelease|RES|INFO|CPOOL> %d#, poolGroup=%d#\n", ObjectID, poolGroup.ObjectID);
- lock (_poolGroupsToRelease) {
- _poolGroupsToRelease.Add(poolGroup);
- }
- #if !MOBILE
- PerformanceCounters.NumberOfInactiveConnectionPoolGroups.Increment();
- #endif
- }
- virtual protected DbConnectionInternal CreateConnection(DbConnectionOptions options, DbConnectionPoolKey poolKey, object poolGroupProviderInfo, DbConnectionPool pool, DbConnection owningConnection, DbConnectionOptions userOptions) {
- return CreateConnection(options, poolKey, poolGroupProviderInfo, pool, owningConnection);
- }
- abstract protected DbConnectionInternal CreateConnection(DbConnectionOptions options, DbConnectionPoolKey poolKey, 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);
- }
- }
|