| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311 |
- //------------------------------------------------------------------------------
- // <copyright file="DbConnectionPoolGroup.cs" company="Microsoft">
- // Copyright (c) Microsoft Corporation. All rights reserved.
- // </copyright>
- // <owner current="true" primary="true">[....]</owner>
- //------------------------------------------------------------------------------
- namespace System.Data.ProviderBase {
- using System;
- using System.Collections.Concurrent;
- using System.Data.Common;
- using System.Diagnostics;
- using System.Threading;
- // set_ConnectionString calls DbConnectionFactory.GetConnectionPoolGroup
- // when not found a new pool entry is created and potentially added
- // DbConnectionPoolGroup starts in the Active state
- // Open calls DbConnectionFactory.GetConnectionPool
- // if the existing pool entry is Disabled, GetConnectionPoolGroup is called for a new entry
- // DbConnectionFactory.GetConnectionPool calls DbConnectionPoolGroup.GetConnectionPool
- // DbConnectionPoolGroup.GetConnectionPool will return pool for the current identity
- // or null if identity is restricted or pooling is disabled or state is disabled at time of add
- // state changes are Active->Active, Idle->Active
- // DbConnectionFactory.PruneConnectionPoolGroups calls Prune
- // which will QueuePoolForRelease on all empty pools
- // and once no pools remain, change state from Active->Idle->Disabled
- // Once Disabled, factory can remove its reference to the pool entry
- sealed internal class DbConnectionPoolGroup {
- private readonly DbConnectionOptions _connectionOptions;
- private readonly DbConnectionPoolKey _poolKey;
- private readonly DbConnectionPoolGroupOptions _poolGroupOptions;
- private ConcurrentDictionary<DbConnectionPoolIdentity, DbConnectionPool> _poolCollection;
- private int _state; // see PoolGroupState* below
- private DbConnectionPoolGroupProviderInfo _providerInfo;
- private DbMetaDataFactory _metaDataFactory;
- private static int _objectTypeCount; // Bid counter
- internal readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount);
- // always lock this before changing _state, we don't want to move out of the 'Disabled' state
- // PoolGroupStateUninitialized = 0;
- private const int PoolGroupStateActive = 1; // initial state, GetPoolGroup from cache, connection Open
- private const int PoolGroupStateIdle = 2; // all pools are pruned via Clear
- private const int PoolGroupStateDisabled = 4; // factory pool entry prunning method
- internal DbConnectionPoolGroup (DbConnectionOptions connectionOptions, DbConnectionPoolKey key, DbConnectionPoolGroupOptions poolGroupOptions) {
- Debug.Assert(null != connectionOptions, "null connection options");
- Debug.Assert(null == poolGroupOptions || ADP.IsWindowsNT, "should not have pooling options on Win9x");
- _connectionOptions = connectionOptions;
- _poolKey = key;
- _poolGroupOptions = poolGroupOptions;
- // always lock this object before changing state
- // HybridDictionary does not create any sub-objects until add
- // so it is safe to use for non-pooled connection as long as
- // we check _poolGroupOptions first
- _poolCollection = new ConcurrentDictionary<DbConnectionPoolIdentity, DbConnectionPool>();
- _state = PoolGroupStateActive; // VSWhidbey 112102
- }
- internal DbConnectionOptions ConnectionOptions {
- get {
- return _connectionOptions;
- }
- }
- internal DbConnectionPoolKey PoolKey {
- get {
- return _poolKey;
- }
- }
- internal DbConnectionPoolGroupProviderInfo ProviderInfo {
- get {
- return _providerInfo;
- }
- set {
- _providerInfo = value;
- if(null!=value) {
- _providerInfo.PoolGroup = this;
- }
- }
- }
- internal bool IsDisabled {
- get {
- return (PoolGroupStateDisabled == _state);
- }
- }
- internal int ObjectID {
- get {
- return _objectID;
- }
- }
- internal DbConnectionPoolGroupOptions PoolGroupOptions {
- get {
- return _poolGroupOptions;
- }
- }
- internal DbMetaDataFactory MetaDataFactory{
- get {
- return _metaDataFactory;
- }
- set {
- _metaDataFactory = value;
- }
- }
- internal int Clear() {
- // must be multi-thread safe with competing calls by Clear and Prune via background thread
- // will return the number of connections in the group after clearing has finished
- // First, note the old collection and create a new collection to be used
- ConcurrentDictionary<DbConnectionPoolIdentity, DbConnectionPool> oldPoolCollection = null;
- lock (this) {
- if (_poolCollection.Count > 0) {
- oldPoolCollection = _poolCollection;
- _poolCollection = new ConcurrentDictionary<DbConnectionPoolIdentity, DbConnectionPool>();
- }
- }
- // Then, if a new collection was created, release the pools from the old collection
- if (oldPoolCollection != null) {
- foreach (var entry in oldPoolCollection) {
- DbConnectionPool pool = entry.Value;
- if (pool != null) {
- //
- DbConnectionFactory connectionFactory = pool.ConnectionFactory;
- #if !MOBILE
- connectionFactory.PerformanceCounters.NumberOfActiveConnectionPools.Decrement();
- #endif
- connectionFactory.QueuePoolForRelease(pool, true);
- }
- }
- }
- // Finally, return the pool collection count - this may be non-zero if something was added while we were clearing
- return _poolCollection.Count;
- }
- internal DbConnectionPool GetConnectionPool(DbConnectionFactory connectionFactory) {
- // When this method returns null it indicates that the connection
- // factory should not use pooling.
- // We don't support connection pooling on Win9x; it lacks too
- // many of the APIs we require.
- // PoolGroupOptions will only be null when we're not supposed to pool
- // connections.
- DbConnectionPool pool = null;
- if (null != _poolGroupOptions) {
- Debug.Assert(ADP.IsWindowsNT, "should not be pooling on Win9x");
- DbConnectionPoolIdentity currentIdentity = DbConnectionPoolIdentity.NoIdentity;
- if (_poolGroupOptions.PoolByIdentity) {
- // if we're pooling by identity (because integrated security is
- // being used for these connections) then we need to go out and
- // search for the connectionPool that matches the current identity.
- currentIdentity = DbConnectionPoolIdentity.GetCurrent();
- // If the current token is restricted in some way, then we must
- // not attempt to pool these connections.
- if (currentIdentity.IsRestricted) {
- currentIdentity = null;
- }
- }
- if (null != currentIdentity) {
- if (!_poolCollection.TryGetValue(currentIdentity, out pool)) { // find the pool
- DbConnectionPoolProviderInfo connectionPoolProviderInfo = connectionFactory.CreateConnectionPoolProviderInfo(this.ConnectionOptions);
- // optimistically create pool, but its callbacks are delayed until after actual add
- DbConnectionPool newPool = new DbConnectionPool(connectionFactory, this, currentIdentity, connectionPoolProviderInfo);
- lock (this) {
- // Did someone already add it to the list?
- if (!_poolCollection.TryGetValue(currentIdentity, out pool)) {
- if (MarkPoolGroupAsActive()) {
- // If we get here, we know for certain that we there isn't
- // a pool that matches the current identity, so we have to
- // add the optimistically created one
- newPool.Startup(); // must start pool before usage
- bool addResult = _poolCollection.TryAdd(currentIdentity, newPool);
- Debug.Assert(addResult, "No other pool with current identity should exist at this point");
- #if !MOBILE
- connectionFactory.PerformanceCounters.NumberOfActiveConnectionPools.Increment();
- #endif
- pool = newPool;
- newPool = null;
- }
- else {
- // else pool entry has been disabled so don't create new pools
- Debug.Assert(PoolGroupStateDisabled == _state, "state should be disabled");
- }
- }
- else {
- // else found an existing pool to use instead
- Debug.Assert(PoolGroupStateActive == _state, "state should be active since a pool exists and lock holds");
- }
- }
- if (null != newPool) {
- // don't need to call connectionFactory.QueuePoolForRelease(newPool) because
- // pool callbacks were delayed and no risk of connections being created
- newPool.Shutdown();
- }
- }
- // the found pool could be in any state
- }
- }
- if (null == pool) {
- lock(this) {
- // keep the pool entry state active when not pooling
- MarkPoolGroupAsActive();
- }
- }
- return pool;
- }
- private bool MarkPoolGroupAsActive() {
- // when getting a connection, make the entry active if it was idle (but not disabled)
- // must always lock this before calling
- if (PoolGroupStateIdle == _state) {
- _state = PoolGroupStateActive;
- Bid.Trace("<prov.DbConnectionPoolGroup.ClearInternal|RES|INFO|CPOOL> %d#, Active\n", ObjectID);
- }
- return (PoolGroupStateActive == _state);
- }
- internal bool Prune() {
- // must only call from DbConnectionFactory.PruneConnectionPoolGroups on background timer thread
- // must lock(DbConnectionFactory._connectionPoolGroups.SyncRoot) before calling ReadyToRemove
- // to avoid conflict with DbConnectionFactory.CreateConnectionPoolGroup replacing pool entry
- lock (this) {
- if (_poolCollection.Count > 0) {
- var newPoolCollection = new ConcurrentDictionary<DbConnectionPoolIdentity, DbConnectionPool>();
- foreach (var entry in _poolCollection) {
- DbConnectionPool pool = entry.Value;
- if (pool != null) {
- //
- // Actually prune the pool if there are no connections in the pool and no errors occurred.
- // Empty pool during pruning indicates zero or low activity, but
- // an error state indicates the pool needs to stay around to
- // throttle new connection attempts.
- if ((!pool.ErrorOccurred) && (0 == pool.Count)) {
- // Order is important here. First we remove the pool
- // from the collection of pools so no one will try
- // to use it while we're processing and finally we put the
- // pool into a list of pools to be released when they
- // are completely empty.
- DbConnectionFactory connectionFactory = pool.ConnectionFactory;
- #if !MOBILE
- connectionFactory.PerformanceCounters.NumberOfActiveConnectionPools.Decrement();
- #endif
- connectionFactory.QueuePoolForRelease(pool, false);
- }
- else {
- newPoolCollection.TryAdd(entry.Key, entry.Value);
- }
- }
- }
- _poolCollection = newPoolCollection;
- }
- // must be pruning thread to change state and no connections
- // otherwise pruning thread risks making entry disabled soon after user calls ClearPool
- if (0 == _poolCollection.Count) {
- if (PoolGroupStateActive == _state) {
- _state = PoolGroupStateIdle;
- Bid.Trace("<prov.DbConnectionPoolGroup.ClearInternal|RES|INFO|CPOOL> %d#, Idle\n", ObjectID);
- }
- else if (PoolGroupStateIdle == _state) {
- _state = PoolGroupStateDisabled;
- Bid.Trace("<prov.DbConnectionPoolGroup.ReadyToRemove|RES|INFO|CPOOL> %d#, Disabled\n", ObjectID);
- }
- }
- return (PoolGroupStateDisabled == _state);
- }
- }
- }
- }
|