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