You've already forked linux-packaging-mono
							
							
		
			
				
	
	
		
			312 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			312 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| //------------------------------------------------------------------------------
 | |
| // <copyright file="DbConnectionPoolGroup.cs" company="Microsoft">
 | |
| //      Copyright (c) Microsoft Corporation.  All rights reserved.
 | |
| // </copyright>
 | |
| // <owner current="true" primary="true">Microsoft</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);
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| }
 |