DbConnectionPool.cs 84 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801
  1. //------------------------------------------------------------------------------
  2. // <copyright file="DbConnectionPool.cs" company="Microsoft">
  3. // Copyright (c) Microsoft Corporation. All rights reserved.
  4. // </copyright>
  5. // <owner current="true" primary="true">[....]</owner>
  6. // <owner current="true" primary="false">[....]</owner>
  7. //------------------------------------------------------------------------------
  8. namespace System.Data.ProviderBase {
  9. using System;
  10. using System.Collections;
  11. using System.Collections.Generic;
  12. using System.Data.Common;
  13. using System.Diagnostics;
  14. using System.Globalization;
  15. using System.Runtime.CompilerServices;
  16. using System.Runtime.ConstrainedExecution;
  17. using System.Runtime.InteropServices;
  18. using System.Security;
  19. using System.Security.Permissions;
  20. using System.Security.Principal;
  21. using System.Threading;
  22. using System.Threading.Tasks;
  23. using SysTx = System.Transactions;
  24. using System.Runtime.Versioning;
  25. using System.Diagnostics.CodeAnalysis;
  26. using System.Collections.Concurrent;
  27. sealed internal class DbConnectionPool {
  28. private enum State {
  29. Initializing,
  30. Running,
  31. ShuttingDown,
  32. }
  33. internal const Bid.ApiGroup PoolerTracePoints = Bid.ApiGroup.Pooling;
  34. // This class is a way to stash our cloned Tx key for later disposal when it's no longer needed.
  35. // We can't get at the key in the dictionary without enumerating entries, so we stash an extra
  36. // copy as part of the value.
  37. sealed private class TransactedConnectionList : List<DbConnectionInternal> {
  38. private SysTx.Transaction _transaction;
  39. internal TransactedConnectionList(int initialAllocation, SysTx.Transaction tx) : base(initialAllocation) {
  40. _transaction = tx;
  41. }
  42. internal void Dispose() {
  43. if (null != _transaction) {
  44. _transaction.Dispose();
  45. }
  46. }
  47. }
  48. sealed class PendingGetConnection {
  49. public PendingGetConnection(long dueTime, DbConnection owner, TaskCompletionSource<DbConnectionInternal> completion, DbConnectionOptions userOptions) {
  50. DueTime = dueTime;
  51. Owner = owner;
  52. Completion = completion;
  53. }
  54. public long DueTime { get; private set; }
  55. public DbConnection Owner { get; private set; }
  56. public TaskCompletionSource<DbConnectionInternal> Completion { get; private set; }
  57. public DbConnectionOptions UserOptions { get; private set; }
  58. }
  59. sealed private class TransactedConnectionPool
  60. {
  61. Dictionary<SysTx.Transaction, TransactedConnectionList> _transactedCxns;
  62. DbConnectionPool _pool;
  63. private static int _objectTypeCount; // Bid counter
  64. internal readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount);
  65. internal TransactedConnectionPool(DbConnectionPool pool)
  66. {
  67. Debug.Assert(null != pool, "null pool?");
  68. _pool = pool;
  69. _transactedCxns = new Dictionary<SysTx.Transaction, TransactedConnectionList> ();
  70. Bid.PoolerTrace("<prov.DbConnectionPool.TransactedConnectionPool.TransactedConnectionPool|RES|CPOOL> %d#, Constructed for connection pool %d#\n", ObjectID, _pool.ObjectID);
  71. }
  72. internal int ObjectID {
  73. get {
  74. return _objectID;
  75. }
  76. }
  77. internal DbConnectionPool Pool {
  78. get {
  79. return _pool;
  80. }
  81. }
  82. internal DbConnectionInternal GetTransactedObject(SysTx.Transaction transaction)
  83. {
  84. Debug.Assert(null != transaction, "null transaction?");
  85. DbConnectionInternal transactedObject = null;
  86. TransactedConnectionList connections;
  87. bool txnFound = false;
  88. lock (_transactedCxns)
  89. {
  90. txnFound = _transactedCxns.TryGetValue ( transaction, out connections );
  91. }
  92. // NOTE: GetTransactedObject is only used when AutoEnlist = True and the ambient transaction
  93. // (Sys.Txns.Txn.Current) is still valid/non-null. This, in turn, means that we don't need
  94. // to worry about a pending asynchronous TransactionCompletedEvent to trigger processing in
  95. // TransactionEnded below and potentially wipe out the connections list underneath us. It
  96. // is similarly alright if a pending addition to the connections list in PutTransactedObject
  97. // below is not completed prior to the lock on the connections object here...getting a new
  98. // connection is probably better than unnecessarily locking
  99. if (txnFound)
  100. {
  101. Debug.Assert ( connections != null );
  102. // synchronize multi-threaded access with PutTransactedObject (TransactionEnded should
  103. // not be a concern, see comments above)
  104. lock ( connections )
  105. {
  106. int i = connections.Count - 1;
  107. if (0 <= i)
  108. {
  109. transactedObject = connections[i];
  110. connections.RemoveAt(i);
  111. }
  112. }
  113. }
  114. if (null != transactedObject) {
  115. Bid.PoolerTrace("<prov.DbConnectionPool.TransactedConnectionPool.GetTransactedObject|RES|CPOOL> %d#, Transaction %d#, Connection %d#, Popped.\n", ObjectID, transaction.GetHashCode(), transactedObject.ObjectID);
  116. }
  117. return transactedObject;
  118. }
  119. internal void PutTransactedObject(SysTx.Transaction transaction, DbConnectionInternal transactedObject) {
  120. Debug.Assert(null != transaction, "null transaction?");
  121. Debug.Assert(null != transactedObject, "null transactedObject?");
  122. TransactedConnectionList connections;
  123. bool txnFound = false;
  124. // NOTE: because TransactionEnded is an asynchronous notification, there's no guarantee
  125. // around the order in which PutTransactionObject and TransactionEnded are called.
  126. lock ( _transactedCxns )
  127. {
  128. // Check if a transacted pool has been created for this transaction
  129. if ( txnFound = _transactedCxns.TryGetValue ( transaction, out connections ) )
  130. {
  131. Debug.Assert ( connections != null );
  132. // synchronize multi-threaded access with GetTransactedObject
  133. lock ( connections )
  134. {
  135. Debug.Assert(0 > connections.IndexOf(transactedObject), "adding to pool a second time?");
  136. Bid.PoolerTrace("<prov.DbConnectionPool.TransactedConnectionPool.PutTransactedObject|RES|CPOOL> %d#, Transaction %d#, Connection %d#, Pushing.\n", ObjectID, transaction.GetHashCode(), transactedObject.ObjectID);
  137. connections.Add(transactedObject);
  138. }
  139. }
  140. }
  141. //
  142. if ( !txnFound )
  143. {
  144. // create the transacted pool, making sure to clone the associated transaction
  145. // for use as a key in our internal dictionary of transactions and connections
  146. SysTx.Transaction transactionClone = null;
  147. TransactedConnectionList newConnections = null;
  148. try
  149. {
  150. transactionClone = transaction.Clone();
  151. newConnections = new TransactedConnectionList(2, transactionClone); // start with only two connections in the list; most times we won't need that many.
  152. lock ( _transactedCxns )
  153. {
  154. // NOTE: in the interim between the locks on the transacted pool (this) during
  155. // execution of this method, another thread (threadB) may have attempted to
  156. // add a different connection to the transacted pool under the same
  157. // transaction. As a result, threadB may have completed creating the
  158. // transacted pool while threadA was processing the above instructions.
  159. if (txnFound = _transactedCxns.TryGetValue(transaction, out connections))
  160. {
  161. Debug.Assert ( connections != null );
  162. // synchronize multi-threaded access with GetTransactedObject
  163. lock ( connections )
  164. {
  165. Debug.Assert(0 > connections.IndexOf(transactedObject), "adding to pool a second time?");
  166. Bid.PoolerTrace("<prov.DbConnectionPool.TransactedConnectionPool.PutTransactedObject|RES|CPOOL> %d#, Transaction %d#, Connection %d#, Pushing.\n", ObjectID, transaction.GetHashCode(), transactedObject.ObjectID);
  167. connections.Add(transactedObject);
  168. }
  169. }
  170. else
  171. {
  172. Bid.PoolerTrace("<prov.DbConnectionPool.TransactedConnectionPool.PutTransactedObject|RES|CPOOL> %d#, Transaction %d#, Connection %d#, Adding List to transacted pool.\n", ObjectID, transaction.GetHashCode(), transactedObject.ObjectID);
  173. // add the connection/transacted object to the list
  174. newConnections.Add ( transactedObject );
  175. _transactedCxns.Add(transactionClone, newConnections);
  176. transactionClone = null; // we've used it -- don't throw it or the TransactedConnectionList that references it away.
  177. }
  178. }
  179. }
  180. finally
  181. {
  182. if (null != transactionClone)
  183. {
  184. if ( newConnections != null )
  185. {
  186. // another thread created the transaction pool and thus the new
  187. // TransactedConnectionList was not used, so dispose of it and
  188. // the transaction clone that it incorporates.
  189. newConnections.Dispose();
  190. }
  191. else
  192. {
  193. // memory allocation for newConnections failed...clean up unused transactionClone
  194. transactionClone.Dispose();
  195. }
  196. }
  197. }
  198. Bid.PoolerTrace("<prov.DbConnectionPool.TransactedConnectionPool.PutTransactedObject|RES|CPOOL> %d#, Transaction %d#, Connection %d#, Added.\n", ObjectID, transaction.GetHashCode(), transactedObject.ObjectID );
  199. }
  200. #if !MOBILE
  201. Pool.PerformanceCounters.NumberOfFreeConnections.Increment();
  202. #endif
  203. }
  204. internal void TransactionEnded(SysTx.Transaction transaction, DbConnectionInternal transactedObject)
  205. {
  206. Bid.PoolerTrace("<prov.DbConnectionPool.TransactedConnectionPool.TransactionEnded|RES|CPOOL> %d#, Transaction %d#, Connection %d#, Transaction Completed\n", ObjectID, transaction.GetHashCode(), transactedObject.ObjectID);
  207. TransactedConnectionList connections;
  208. int entry = -1;
  209. // NOTE: because TransactionEnded is an asynchronous notification, there's no guarantee
  210. // around the order in which PutTransactionObject and TransactionEnded are called. As
  211. // such, it is possible that the transaction does not yet have a pool created.
  212. //
  213. lock ( _transactedCxns )
  214. {
  215. if (_transactedCxns.TryGetValue(transaction, out connections))
  216. {
  217. Debug.Assert ( connections != null );
  218. bool shouldDisposeConnections = false;
  219. // Lock connections to avoid conflict with GetTransactionObject
  220. lock (connections)
  221. {
  222. entry = connections.IndexOf(transactedObject);
  223. if ( entry >= 0 )
  224. {
  225. connections.RemoveAt(entry);
  226. }
  227. // Once we've completed all the ended notifications, we can
  228. // safely remove the list from the transacted pool.
  229. if (0 >= connections.Count)
  230. {
  231. Bid.PoolerTrace("<prov.DbConnectionPool.TransactedConnectionPool.TransactionEnded|RES|CPOOL> %d#, Transaction %d#, Removing List from transacted pool.\n", ObjectID, transaction.GetHashCode());
  232. _transactedCxns.Remove(transaction);
  233. // we really need to dispose our connection list; it may have
  234. // native resources via the tx and GC may not happen soon enough.
  235. shouldDisposeConnections = true;
  236. }
  237. }
  238. if (shouldDisposeConnections) {
  239. connections.Dispose();
  240. }
  241. }
  242. else
  243. {
  244. //Debug.Assert ( false, "TransactionCompletedEvent fired before PutTransactedObject put the connection in the transacted pool." );
  245. 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 );
  246. }
  247. }
  248. // If (and only if) we found the connection in the list of
  249. // connections, we'll put it back...
  250. if (0 <= entry)
  251. {
  252. #if !MOBILE
  253. Pool.PerformanceCounters.NumberOfFreeConnections.Decrement();
  254. #endif
  255. Pool.PutObjectFromTransactedPool(transactedObject);
  256. }
  257. }
  258. }
  259. private sealed class PoolWaitHandles : DbBuffer {
  260. private readonly Semaphore _poolSemaphore;
  261. private readonly ManualResetEvent _errorEvent;
  262. // Using a Mutex requires ThreadAffinity because SQL CLR can swap
  263. // the underlying Win32 thread associated with a managed thread in preemptive mode.
  264. // Using an AutoResetEvent does not have that complication.
  265. private readonly Semaphore _creationSemaphore;
  266. private readonly SafeHandle _poolHandle;
  267. private readonly SafeHandle _errorHandle;
  268. private readonly SafeHandle _creationHandle;
  269. private readonly int _releaseFlags;
  270. [ResourceExposure(ResourceScope.None)] // SxS: this method does not create named objects
  271. [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
  272. internal PoolWaitHandles() : base(3*IntPtr.Size) {
  273. bool mustRelease1 = false, mustRelease2 = false, mustRelease3 = false;
  274. _poolSemaphore = new Semaphore(0, MAX_Q_SIZE);
  275. _errorEvent = new ManualResetEvent(false);
  276. _creationSemaphore = new Semaphore(1, 1);
  277. RuntimeHelpers.PrepareConstrainedRegions();
  278. try {
  279. // because SafeWaitHandle doesn't have reliability contract
  280. _poolHandle = _poolSemaphore.SafeWaitHandle;
  281. _errorHandle = _errorEvent.SafeWaitHandle;
  282. _creationHandle = _creationSemaphore.SafeWaitHandle;
  283. _poolHandle.DangerousAddRef(ref mustRelease1);
  284. _errorHandle.DangerousAddRef(ref mustRelease2);
  285. _creationHandle.DangerousAddRef(ref mustRelease3);
  286. Debug.Assert(0 == SEMAPHORE_HANDLE, "SEMAPHORE_HANDLE");
  287. Debug.Assert(1 == ERROR_HANDLE, "ERROR_HANDLE");
  288. Debug.Assert(2 == CREATION_HANDLE, "CREATION_HANDLE");
  289. WriteIntPtr(SEMAPHORE_HANDLE*IntPtr.Size, _poolHandle.DangerousGetHandle());
  290. WriteIntPtr(ERROR_HANDLE*IntPtr.Size, _errorHandle.DangerousGetHandle());
  291. WriteIntPtr(CREATION_HANDLE*IntPtr.Size, _creationHandle.DangerousGetHandle());
  292. }
  293. finally {
  294. if (mustRelease1) {
  295. _releaseFlags |= 1;
  296. }
  297. if (mustRelease2) {
  298. _releaseFlags |= 2;
  299. }
  300. if (mustRelease3) {
  301. _releaseFlags |= 4;
  302. }
  303. }
  304. }
  305. internal SafeHandle CreationHandle {
  306. [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
  307. get { return _creationHandle; }
  308. }
  309. internal Semaphore CreationSemaphore {
  310. get { return _creationSemaphore; }
  311. }
  312. internal ManualResetEvent ErrorEvent {
  313. get { return _errorEvent; }
  314. }
  315. internal Semaphore PoolSemaphore {
  316. get { return _poolSemaphore; }
  317. }
  318. protected override bool ReleaseHandle() {
  319. // NOTE: The SafeHandle class guarantees this will be called exactly once.
  320. // we know we can touch these other managed objects because of our original DangerousAddRef
  321. if (0 != (1 & _releaseFlags)) {
  322. _poolHandle.DangerousRelease();
  323. }
  324. if (0 != (2 & _releaseFlags)) {
  325. _errorHandle.DangerousRelease();
  326. }
  327. if (0 != (4 & _releaseFlags)) {
  328. _creationHandle.DangerousRelease();
  329. }
  330. return base.ReleaseHandle();
  331. }
  332. }
  333. private const int MAX_Q_SIZE = (int)0x00100000;
  334. // The order of these is important; we want the WaitAny call to be signaled
  335. // for a free object before a creation signal. Only the index first signaled
  336. // object is returned from the WaitAny call.
  337. private const int SEMAPHORE_HANDLE = (int)0x0;
  338. private const int ERROR_HANDLE = (int)0x1;
  339. private const int CREATION_HANDLE = (int)0x2;
  340. private const int BOGUS_HANDLE = (int)0x3;
  341. private const int WAIT_OBJECT_0 = 0;
  342. private const int WAIT_TIMEOUT = (int)0x102;
  343. private const int WAIT_ABANDONED = (int)0x80;
  344. private const int WAIT_FAILED = -1;
  345. private const int ERROR_WAIT_DEFAULT = 5 * 1000; // 5 seconds
  346. // we do want a testable, repeatable set of generated random numbers
  347. private static readonly Random _random = new Random(5101977); // Value obtained from Dave Driver
  348. private readonly int _cleanupWait;
  349. private readonly DbConnectionPoolIdentity _identity;
  350. private readonly DbConnectionFactory _connectionFactory;
  351. private readonly DbConnectionPoolGroup _connectionPoolGroup;
  352. private readonly DbConnectionPoolGroupOptions _connectionPoolGroupOptions;
  353. private DbConnectionPoolProviderInfo _connectionPoolProviderInfo;
  354. /// <summary>
  355. /// The private member which carries the set of authenticationcontexts for this pool (based on the user's identity).
  356. /// </summary>
  357. private readonly ConcurrentDictionary<DbConnectionPoolAuthenticationContextKey, DbConnectionPoolAuthenticationContext> _pooledDbAuthenticationContexts;
  358. private State _state;
  359. private readonly ConcurrentStack<DbConnectionInternal> _stackOld = new ConcurrentStack<DbConnectionInternal>();
  360. private readonly ConcurrentStack<DbConnectionInternal> _stackNew = new ConcurrentStack<DbConnectionInternal>();
  361. private readonly ConcurrentQueue<PendingGetConnection> _pendingOpens = new ConcurrentQueue<PendingGetConnection>();
  362. private int _pendingOpensWaiting = 0;
  363. private readonly WaitCallback _poolCreateRequest;
  364. private int _waitCount;
  365. private readonly PoolWaitHandles _waitHandles;
  366. private Exception _resError;
  367. private volatile bool _errorOccurred;
  368. private int _errorWait;
  369. private Timer _errorTimer;
  370. private Timer _cleanupTimer;
  371. private readonly TransactedConnectionPool _transactedConnectionPool;
  372. private readonly List<DbConnectionInternal> _objectList;
  373. private int _totalObjects;
  374. private static int _objectTypeCount; // Bid counter
  375. internal readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount);
  376. // only created by DbConnectionPoolGroup.GetConnectionPool
  377. internal DbConnectionPool(
  378. DbConnectionFactory connectionFactory,
  379. DbConnectionPoolGroup connectionPoolGroup,
  380. DbConnectionPoolIdentity identity,
  381. DbConnectionPoolProviderInfo connectionPoolProviderInfo ) {
  382. Debug.Assert(ADP.IsWindowsNT, "Attempting to construct a connection pool on Win9x?");
  383. Debug.Assert(null != connectionPoolGroup, "null connectionPoolGroup");
  384. if ((null != identity) && identity.IsRestricted) {
  385. throw ADP.InternalError(ADP.InternalErrorCode.AttemptingToPoolOnRestrictedToken);
  386. }
  387. _state= State.Initializing;
  388. lock(_random) { // Random.Next is not thread-safe
  389. _cleanupWait = _random.Next(12, 24)*10*1000; // 2-4 minutes in 10 sec intervals, WebData 103603
  390. }
  391. _connectionFactory = connectionFactory;
  392. _connectionPoolGroup = connectionPoolGroup;
  393. _connectionPoolGroupOptions = connectionPoolGroup.PoolGroupOptions;
  394. _connectionPoolProviderInfo = connectionPoolProviderInfo;
  395. _identity = identity;
  396. _waitHandles = new PoolWaitHandles();
  397. _errorWait = ERROR_WAIT_DEFAULT;
  398. _errorTimer = null; // No error yet.
  399. _objectList = new List<DbConnectionInternal>(MaxPoolSize);
  400. _pooledDbAuthenticationContexts = new ConcurrentDictionary<DbConnectionPoolAuthenticationContextKey, DbConnectionPoolAuthenticationContext>(concurrencyLevel: 4 * Environment.ProcessorCount /* default value in ConcurrentDictionary*/,
  401. capacity: 2);
  402. if(ADP.IsPlatformNT5) {
  403. _transactedConnectionPool = new TransactedConnectionPool(this);
  404. }
  405. _poolCreateRequest = new WaitCallback(PoolCreateRequest); // used by CleanupCallback
  406. _state = State.Running;
  407. Bid.PoolerTrace("<prov.DbConnectionPool.DbConnectionPool|RES|CPOOL> %d#, Constructed.\n", ObjectID);
  408. //_cleanupTimer & QueuePoolCreateRequest is delayed until DbConnectionPoolGroup calls
  409. // StartBackgroundCallbacks after pool is actually in the collection
  410. }
  411. private int CreationTimeout {
  412. get { return PoolGroupOptions.CreationTimeout; }
  413. }
  414. internal int Count {
  415. get { return _totalObjects; }
  416. }
  417. internal DbConnectionFactory ConnectionFactory {
  418. get { return _connectionFactory; }
  419. }
  420. internal bool ErrorOccurred {
  421. get { return _errorOccurred; }
  422. }
  423. private bool HasTransactionAffinity {
  424. get { return PoolGroupOptions.HasTransactionAffinity; }
  425. }
  426. internal TimeSpan LoadBalanceTimeout {
  427. get { return PoolGroupOptions.LoadBalanceTimeout; }
  428. }
  429. private bool NeedToReplenish {
  430. get {
  431. if (State.Running != _state) // SQL BU DT 364595 - don't allow connection create when not running.
  432. return false;
  433. int totalObjects = Count;
  434. if (totalObjects >= MaxPoolSize)
  435. return false;
  436. if (totalObjects < MinPoolSize)
  437. return true;
  438. int freeObjects = (_stackNew.Count + _stackOld.Count);
  439. int waitingRequests = _waitCount;
  440. bool needToReplenish = (freeObjects < waitingRequests) || ((freeObjects == waitingRequests) && (totalObjects > 1));
  441. return needToReplenish;
  442. }
  443. }
  444. internal DbConnectionPoolIdentity Identity {
  445. get { return _identity; }
  446. }
  447. internal bool IsRunning {
  448. get { return State.Running == _state; }
  449. }
  450. private int MaxPoolSize {
  451. get { return PoolGroupOptions.MaxPoolSize; }
  452. }
  453. private int MinPoolSize {
  454. get { return PoolGroupOptions.MinPoolSize; }
  455. }
  456. internal int ObjectID {
  457. get {
  458. return _objectID;
  459. }
  460. }
  461. internal DbConnectionPoolCounters PerformanceCounters {
  462. get { return _connectionFactory.PerformanceCounters; }
  463. }
  464. internal DbConnectionPoolGroup PoolGroup {
  465. get { return _connectionPoolGroup; }
  466. }
  467. internal DbConnectionPoolGroupOptions PoolGroupOptions {
  468. get { return _connectionPoolGroupOptions; }
  469. }
  470. internal DbConnectionPoolProviderInfo ProviderInfo {
  471. get { return _connectionPoolProviderInfo; }
  472. }
  473. /// <summary>
  474. /// Return the pooled authentication contexts.
  475. /// </summary>
  476. internal ConcurrentDictionary<DbConnectionPoolAuthenticationContextKey, DbConnectionPoolAuthenticationContext> AuthenticationContexts
  477. {
  478. get
  479. {
  480. return _pooledDbAuthenticationContexts;
  481. }
  482. }
  483. internal bool UseLoadBalancing {
  484. get { return PoolGroupOptions.UseLoadBalancing; }
  485. }
  486. private bool UsingIntegrateSecurity {
  487. get { return (null != _identity && DbConnectionPoolIdentity.NoIdentity != _identity); }
  488. }
  489. private void CleanupCallback(Object state) {
  490. // Called when the cleanup-timer ticks over.
  491. // This is the automatic prunning method. Every period, we will
  492. // perform a two-step process:
  493. //
  494. // First, for each free object above MinPoolSize, we will obtain a
  495. // semaphore representing one object and destroy one from old stack.
  496. // We will continue this until we either reach MinPoolSize, we are
  497. // unable to obtain a free object, or we have exhausted all the
  498. // objects on the old stack.
  499. //
  500. // Second we move all free objects on the new stack to the old stack.
  501. // So, every period the objects on the old stack are destroyed and
  502. // the objects on the new stack are pushed to the old stack. All
  503. // objects that are currently out and in use are not on either stack.
  504. //
  505. // With this logic, objects are pruned from the pool if unused for
  506. // at least one period but not more than two periods.
  507. Bid.PoolerTrace("<prov.DbConnectionPool.CleanupCallback|RES|INFO|CPOOL> %d#\n", ObjectID);
  508. // Destroy free objects that put us above MinPoolSize from old stack.
  509. while(Count > MinPoolSize) { // While above MinPoolSize...
  510. if (_waitHandles.PoolSemaphore.WaitOne(0, false) /* != WAIT_TIMEOUT */) {
  511. // We obtained a objects from the semaphore.
  512. DbConnectionInternal obj;
  513. if (_stackOld.TryPop(out obj)) {
  514. Debug.Assert(obj != null, "null connection is not expected");
  515. // If we obtained one from the old stack, destroy it.
  516. #if !MOBILE
  517. PerformanceCounters.NumberOfFreeConnections.Decrement();
  518. #endif
  519. // Transaction roots must survive even aging out (TxEnd event will clean them up).
  520. bool shouldDestroy = true;
  521. lock (obj) { // Lock to prevent race condition window between IsTransactionRoot and shouldDestroy assignment
  522. if (obj.IsTransactionRoot) {
  523. shouldDestroy = false;
  524. }
  525. }
  526. // !!!!!!!!!! WARNING !!!!!!!!!!!!!
  527. // ONLY touch obj after lock release if shouldDestroy is false!!! Otherwise, it may be destroyed
  528. // by transaction-end thread!
  529. // Note that there is a minor race condition between this task and the transaction end event, if the latter runs
  530. // between the lock above and the SetInStasis call below. The reslult is that the stasis counter may be
  531. // incremented without a corresponding decrement (the transaction end task is normally expected
  532. // to decrement, but will only do so if the stasis flag is set when it runs). I've minimized the size
  533. // of the window, but we aren't totally eliminating it due to SetInStasis needing to do bid tracing, which
  534. // we don't want to do under this lock, if possible. It should be possible to eliminate this race condition with
  535. // more substantial re-architecture of the pool, but we don't have the time to do that work for the current release.
  536. if (shouldDestroy) {
  537. DestroyObject(obj);
  538. }
  539. else {
  540. obj.SetInStasis();
  541. }
  542. }
  543. else {
  544. // Else we exhausted the old stack (the object the
  545. // semaphore represents is on the new stack), so break.
  546. _waitHandles.PoolSemaphore.Release(1);
  547. break;
  548. }
  549. }
  550. else {
  551. break;
  552. }
  553. }
  554. // Push to the old-stack. For each free object, move object from
  555. // new stack to old stack.
  556. if(_waitHandles.PoolSemaphore.WaitOne(0, false) /* != WAIT_TIMEOUT */) {
  557. for(;;) {
  558. DbConnectionInternal obj;
  559. if (!_stackNew.TryPop(out obj))
  560. break;
  561. Debug.Assert(obj != null, "null connection is not expected");
  562. Bid.PoolerTrace("<prov.DbConnectionPool.CleanupCallback|RES|INFO|CPOOL> %d#, ChangeStacks=%d#\n", ObjectID, obj.ObjectID);
  563. Debug.Assert(!obj.IsEmancipated, "pooled object not in pool");
  564. Debug.Assert(obj.CanBePooled, "pooled object is not poolable");
  565. _stackOld.Push(obj);
  566. }
  567. _waitHandles.PoolSemaphore.Release(1);
  568. }
  569. // Queue up a request to bring us up to MinPoolSize
  570. QueuePoolCreateRequest();
  571. }
  572. internal void Clear() {
  573. Bid.PoolerTrace("<prov.DbConnectionPool.Clear|RES|CPOOL> %d#, Clearing.\n", ObjectID);
  574. DbConnectionInternal obj;
  575. // First, quickly doom everything.
  576. lock(_objectList) {
  577. int count = _objectList.Count;
  578. for (int i = 0; i < count; ++i) {
  579. obj = _objectList[i];
  580. if (null != obj) {
  581. obj.DoNotPoolThisConnection();
  582. }
  583. }
  584. }
  585. // Second, dispose of all the free connections.
  586. while (_stackNew.TryPop(out obj)) {
  587. Debug.Assert(obj != null, "null connection is not expected");
  588. #if !MOBILE
  589. PerformanceCounters.NumberOfFreeConnections.Decrement();
  590. #endif
  591. DestroyObject(obj);
  592. }
  593. while (_stackOld.TryPop(out obj)) {
  594. Debug.Assert(obj != null, "null connection is not expected");
  595. #if !MOBILE
  596. PerformanceCounters.NumberOfFreeConnections.Decrement();
  597. #endif
  598. DestroyObject(obj);
  599. }
  600. // Finally, reclaim everything that's emancipated (which, because
  601. // it's been doomed, will cause it to be disposed of as well)
  602. ReclaimEmancipatedObjects();
  603. Bid.PoolerTrace("<prov.DbConnectionPool.Clear|RES|CPOOL> %d#, Cleared.\n", ObjectID);
  604. }
  605. private Timer CreateCleanupTimer() {
  606. return (new Timer(new TimerCallback(this.CleanupCallback), null, _cleanupWait, _cleanupWait));
  607. }
  608. private DbConnectionInternal CreateObject(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection) {
  609. DbConnectionInternal newObj = null;
  610. try {
  611. newObj = _connectionFactory.CreatePooledConnection(this, owningObject, _connectionPoolGroup.ConnectionOptions, _connectionPoolGroup.PoolKey, userOptions);
  612. if (null == newObj) {
  613. throw ADP.InternalError(ADP.InternalErrorCode.CreateObjectReturnedNull); // CreateObject succeeded, but null object
  614. }
  615. if (!newObj.CanBePooled) {
  616. throw ADP.InternalError(ADP.InternalErrorCode.NewObjectCannotBePooled); // CreateObject succeeded, but non-poolable object
  617. }
  618. newObj.PrePush(null);
  619. lock (_objectList) {
  620. if ((oldConnection != null) && (oldConnection.Pool == this)) {
  621. _objectList.Remove(oldConnection);
  622. }
  623. _objectList.Add(newObj);
  624. _totalObjects = _objectList.Count;
  625. #if !MOBILE
  626. PerformanceCounters.NumberOfPooledConnections.Increment(); //
  627. #endif
  628. }
  629. // If the old connection belonged to another pool, we need to remove it from that
  630. if (oldConnection != null) {
  631. var oldConnectionPool = oldConnection.Pool;
  632. if (oldConnectionPool != null && oldConnectionPool != this) {
  633. Debug.Assert(oldConnectionPool._state == State.ShuttingDown, "Old connections pool should be shutting down");
  634. lock (oldConnectionPool._objectList) {
  635. oldConnectionPool._objectList.Remove(oldConnection);
  636. oldConnectionPool._totalObjects = oldConnectionPool._objectList.Count;
  637. }
  638. }
  639. }
  640. Bid.PoolerTrace("<prov.DbConnectionPool.CreateObject|RES|CPOOL> %d#, Connection %d#, Added to pool.\n", ObjectID, newObj.ObjectID);
  641. // Reset the error wait:
  642. _errorWait = ERROR_WAIT_DEFAULT;
  643. }
  644. catch(Exception e) {
  645. //
  646. if (!ADP.IsCatchableExceptionType(e)) {
  647. throw;
  648. }
  649. ADP.TraceExceptionForCapture(e);
  650. newObj = null; // set to null, so we do not return bad new object
  651. // Failed to create instance
  652. _resError = e;
  653. // VSTFDEVDIV 479561: Make sure the timer starts even if ThreadAbort occurs after setting the ErrorEvent.
  654. // timer allocation has to be done out of CER block
  655. Timer t = new Timer(new TimerCallback(this.ErrorCallback), null, Timeout.Infinite, Timeout.Infinite);
  656. bool timerIsNotDisposed;
  657. RuntimeHelpers.PrepareConstrainedRegions();
  658. try{} finally {
  659. _waitHandles.ErrorEvent.Set();
  660. _errorOccurred = true;
  661. // Enable the timer.
  662. // Note that the timer is created to allow periodic invocation. If ThreadAbort occurs in the middle of ErrorCallback,
  663. // the timer will restart. Otherwise, the timer callback (ErrorCallback) destroys the timer after resetting the error to avoid second callback.
  664. _errorTimer = t;
  665. timerIsNotDisposed = t.Change(_errorWait, _errorWait);
  666. }
  667. Debug.Assert(timerIsNotDisposed, "ErrorCallback timer has been disposed");
  668. if (30000 < _errorWait) {
  669. _errorWait = 60000;
  670. }
  671. else {
  672. _errorWait *= 2;
  673. }
  674. throw;
  675. }
  676. return newObj;
  677. }
  678. private void DeactivateObject(DbConnectionInternal obj)
  679. {
  680. Bid.PoolerTrace("<prov.DbConnectionPool.DeactivateObject|RES|CPOOL> %d#, Connection %d#, Deactivating.\n", ObjectID, obj.ObjectID);
  681. obj.DeactivateConnection(); // we presume this operation is safe outside of a lock...
  682. bool returnToGeneralPool = false;
  683. bool destroyObject = false;
  684. bool rootTxn = false;
  685. if ( obj.IsConnectionDoomed )
  686. {
  687. // the object is not fit for reuse -- just dispose of it.
  688. destroyObject = true;
  689. }
  690. else
  691. {
  692. // NOTE: constructor should ensure that current state cannot be State.Initializing, so it can only
  693. // be State.Running or State.ShuttingDown
  694. Debug.Assert ( _state == State.Running || _state == State.ShuttingDown );
  695. lock (obj)
  696. {
  697. // A connection with a delegated transaction cannot currently
  698. // be returned to a different customer until the transaction
  699. // actually completes, so we send it into Stasis -- the SysTx
  700. // transaction object will ensure that it is owned (not lost),
  701. // and it will be certain to put it back into the pool.
  702. if ( _state == State.ShuttingDown )
  703. {
  704. if ( obj.IsTransactionRoot )
  705. {
  706. // SQLHotfix# 50003503 - connections that are affiliated with a
  707. // root transaction and that also happen to be in a connection
  708. // pool that is being shutdown need to be put in stasis so that
  709. // the root transaction isn't effectively orphaned with no
  710. // means to promote itself to a full delegated transaction or
  711. // Commit or Rollback
  712. obj.SetInStasis();
  713. rootTxn = true;
  714. }
  715. else
  716. {
  717. // connection is being closed and the pool has been marked as shutting
  718. // down, so destroy this object.
  719. destroyObject = true;
  720. }
  721. }
  722. else
  723. {
  724. if ( obj.IsNonPoolableTransactionRoot )
  725. {
  726. obj.SetInStasis();
  727. rootTxn = true;
  728. }
  729. else if ( obj.CanBePooled )
  730. {
  731. // We must put this connection into the transacted pool
  732. // while inside a lock to prevent a race condition with
  733. // the transaction asyncronously completing on a second
  734. // thread.
  735. SysTx.Transaction transaction = obj.EnlistedTransaction;
  736. if (null != transaction)
  737. {
  738. // NOTE: we're not locking on _state, so it's possible that its
  739. // value could change between the conditional check and here.
  740. // Although perhaps not ideal, this is OK because the
  741. // DelegatedTransactionEnded event will clean up the
  742. // connection appropriately regardless of the pool state.
  743. Debug.Assert ( _transactedConnectionPool != null, "Transacted connection pool was not expected to be null.");
  744. _transactedConnectionPool.PutTransactedObject(transaction, obj);
  745. rootTxn = true;
  746. }
  747. else
  748. {
  749. // return to general pool
  750. returnToGeneralPool = true;
  751. }
  752. }
  753. else
  754. {
  755. if ( obj.IsTransactionRoot && !obj.IsConnectionDoomed )
  756. {
  757. // SQLHotfix# 50003503 - if the object cannot be pooled but is a transaction
  758. // root, then we must have hit one of two race conditions:
  759. // 1) PruneConnectionPoolGroups shutdown the pool and marked this connection
  760. // as non-poolable while we were processing within this lock
  761. // 2) The LoadBalancingTimeout expired on this connection and marked this
  762. // connection as DoNotPool.
  763. //
  764. // This connection needs to be put in stasis so that the root transaction isn't
  765. // effectively orphaned with no means to promote itself to a full delegated
  766. // transaction or Commit or Rollback
  767. obj.SetInStasis();
  768. rootTxn = true;
  769. }
  770. else
  771. {
  772. // object is not fit for reuse -- just dispose of it
  773. destroyObject = true;
  774. }
  775. }
  776. }
  777. }
  778. }
  779. if (returnToGeneralPool)
  780. {
  781. // Only push the connection into the general pool if we didn't
  782. // already push it onto the transacted pool, put it into stasis,
  783. // or want to destroy it.
  784. Debug.Assert ( destroyObject == false );
  785. PutNewObject(obj);
  786. }
  787. else if ( destroyObject )
  788. {
  789. // VSTFDEVDIV# 479556 - connections that have been marked as no longer
  790. // poolable (e.g. exceeded their connection lifetime) are not, in fact,
  791. // returned to the general pool
  792. DestroyObject(obj);
  793. QueuePoolCreateRequest();
  794. }
  795. //-------------------------------------------------------------------------------------
  796. // postcondition
  797. // ensure that the connection was processed
  798. Debug.Assert ( rootTxn == true || returnToGeneralPool == true || destroyObject == true );
  799. //
  800. }
  801. internal void DestroyObject(DbConnectionInternal obj) {
  802. // A connection with a delegated transaction cannot be disposed of
  803. // until the delegated transaction has actually completed. Instead,
  804. // we simply leave it alone; when the transaction completes, it will
  805. // come back through PutObjectFromTransactedPool, which will call us
  806. // again.
  807. if (obj.IsTxRootWaitingForTxEnd) {
  808. Bid.PoolerTrace("<prov.DbConnectionPool.DestroyObject|RES|CPOOL> %d#, Connection %d#, Has Delegated Transaction, waiting to Dispose.\n", ObjectID, obj.ObjectID);
  809. }
  810. else {
  811. Bid.PoolerTrace("<prov.DbConnectionPool.DestroyObject|RES|CPOOL> %d#, Connection %d#, Removing from pool.\n", ObjectID, obj.ObjectID);
  812. bool removed = false;
  813. lock (_objectList) {
  814. removed = _objectList.Remove(obj);
  815. Debug.Assert(removed, "attempt to DestroyObject not in list");
  816. _totalObjects = _objectList.Count;
  817. }
  818. if (removed) {
  819. Bid.PoolerTrace("<prov.DbConnectionPool.DestroyObject|RES|CPOOL> %d#, Connection %d#, Removed from pool.\n", ObjectID, obj.ObjectID);
  820. #if !MOBILE
  821. PerformanceCounters.NumberOfPooledConnections.Decrement();
  822. #endif
  823. }
  824. obj.Dispose();
  825. Bid.PoolerTrace("<prov.DbConnectionPool.DestroyObject|RES|CPOOL> %d#, Connection %d#, Disposed.\n", ObjectID, obj.ObjectID);
  826. #if !MOBILE
  827. PerformanceCounters.HardDisconnectsPerSecond.Increment();
  828. #endif
  829. }
  830. }
  831. private void ErrorCallback(Object state) {
  832. Bid.PoolerTrace("<prov.DbConnectionPool.ErrorCallback|RES|CPOOL> %d#, Resetting Error handling.\n", ObjectID);
  833. _errorOccurred = false;
  834. _waitHandles.ErrorEvent.Reset();
  835. // the error state is cleaned, destroy the timer to avoid periodic invocation
  836. Timer t = _errorTimer;
  837. _errorTimer = null;
  838. if (t != null) {
  839. t.Dispose(); // Cancel timer request.
  840. }
  841. }
  842. private Exception TryCloneCachedException()
  843. // Cached exception can be of any type, so is not always cloneable.
  844. // This functions clones SqlException
  845. // OleDb and Odbc connections are not passing throw this code
  846. {
  847. if (_resError==null)
  848. return null;
  849. if (_resError.GetType()==typeof(SqlClient.SqlException))
  850. return ((SqlClient.SqlException)_resError).InternalClone();
  851. return _resError;
  852. }
  853. void WaitForPendingOpen() {
  854. Debug.Assert(!Thread.CurrentThread.IsThreadPoolThread, "This thread may block for a long time. Threadpool threads should not be used.");
  855. PendingGetConnection next;
  856. do {
  857. bool started = false;
  858. RuntimeHelpers.PrepareConstrainedRegions();
  859. try {
  860. RuntimeHelpers.PrepareConstrainedRegions();
  861. try { }
  862. finally {
  863. started = Interlocked.CompareExchange(ref _pendingOpensWaiting, 1, 0) == 0;
  864. }
  865. if (!started) {
  866. return;
  867. }
  868. while (_pendingOpens.TryDequeue(out next)) {
  869. if (next.Completion.Task.IsCompleted) {
  870. continue;
  871. }
  872. uint delay;
  873. if (next.DueTime == Timeout.Infinite) {
  874. delay = unchecked((uint)Timeout.Infinite);
  875. }
  876. else {
  877. delay = (uint)Math.Max(ADP.TimerRemainingMilliseconds(next.DueTime), 0);
  878. }
  879. DbConnectionInternal connection = null;
  880. bool timeout = false;
  881. Exception caughtException = null;
  882. RuntimeHelpers.PrepareConstrainedRegions();
  883. try {
  884. #if DEBUG
  885. System.Data.SqlClient.TdsParser.ReliabilitySection tdsReliabilitySection = new System.Data.SqlClient.TdsParser.ReliabilitySection();
  886. RuntimeHelpers.PrepareConstrainedRegions();
  887. try {
  888. tdsReliabilitySection.Start();
  889. #else
  890. {
  891. #endif //DEBUG
  892. bool allowCreate = true;
  893. bool onlyOneCheckConnection = false;
  894. ADP.SetCurrentTransaction(next.Completion.Task.AsyncState as Transactions.Transaction);
  895. timeout = !TryGetConnection(next.Owner, delay, allowCreate, onlyOneCheckConnection, next.UserOptions, out connection);
  896. }
  897. #if DEBUG
  898. finally {
  899. tdsReliabilitySection.Stop();
  900. }
  901. #endif //DEBUG
  902. }
  903. catch (System.OutOfMemoryException) {
  904. if (connection != null) { connection.DoomThisConnection(); }
  905. throw;
  906. }
  907. catch (System.StackOverflowException) {
  908. if (connection != null) { connection.DoomThisConnection(); }
  909. throw;
  910. }
  911. catch (System.Threading.ThreadAbortException) {
  912. if (connection != null) { connection.DoomThisConnection(); }
  913. throw;
  914. }
  915. catch (Exception e) {
  916. caughtException = e;
  917. }
  918. if (caughtException != null) {
  919. next.Completion.TrySetException(caughtException);
  920. }
  921. else if (timeout) {
  922. next.Completion.TrySetException(ADP.ExceptionWithStackTrace(ADP.PooledOpenTimeout()));
  923. }
  924. else {
  925. Debug.Assert(connection != null, "connection should never be null in success case");
  926. if (!next.Completion.TrySetResult(connection)) {
  927. // if the completion was cancelled, lets try and get this connection back for the next try
  928. PutObject(connection, next.Owner);
  929. }
  930. }
  931. }
  932. }
  933. finally {
  934. if (started) {
  935. Interlocked.Exchange(ref _pendingOpensWaiting, 0);
  936. }
  937. }
  938. } while (_pendingOpens.TryPeek(out next));
  939. }
  940. internal bool TryGetConnection(DbConnection owningObject, TaskCompletionSource<DbConnectionInternal> retry, DbConnectionOptions userOptions, out DbConnectionInternal connection) {
  941. uint waitForMultipleObjectsTimeout = 0;
  942. bool allowCreate = false;
  943. if (retry == null) {
  944. waitForMultipleObjectsTimeout = (uint)CreationTimeout;
  945. // VSTFDEVDIV 445531: set the wait timeout to INFINITE (-1) if the SQL connection timeout is 0 (== infinite)
  946. if (waitForMultipleObjectsTimeout == 0)
  947. waitForMultipleObjectsTimeout = unchecked((uint)Timeout.Infinite);
  948. allowCreate = true;
  949. }
  950. if (_state != State.Running) {
  951. Bid.PoolerTrace("<prov.DbConnectionPool.GetConnection|RES|CPOOL> %d#, DbConnectionInternal State != Running.\n", ObjectID);
  952. connection = null;
  953. return true;
  954. }
  955. bool onlyOneCheckConnection = true;
  956. if (TryGetConnection(owningObject, waitForMultipleObjectsTimeout, allowCreate, onlyOneCheckConnection, userOptions, out connection)) {
  957. return true;
  958. }
  959. else if (retry == null) {
  960. // timed out on a [....] call
  961. return true;
  962. }
  963. var pendingGetConnection =
  964. new PendingGetConnection(
  965. CreationTimeout == 0 ? Timeout.Infinite : ADP.TimerCurrent() + ADP.TimerFromSeconds(CreationTimeout/1000),
  966. owningObject,
  967. retry,
  968. userOptions);
  969. _pendingOpens.Enqueue(pendingGetConnection);
  970. // it is better to StartNew too many times than not enough
  971. if (_pendingOpensWaiting == 0) {
  972. Thread waitOpenThread = new Thread(WaitForPendingOpen);
  973. waitOpenThread.IsBackground = true;
  974. waitOpenThread.Start();
  975. }
  976. connection = null;
  977. return false;
  978. }
  979. [SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods")] // copied from Triaged.cs
  980. [ResourceExposure(ResourceScope.None)] // SxS: this method does not expose resources
  981. [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
  982. private bool TryGetConnection(DbConnection owningObject, uint waitForMultipleObjectsTimeout, bool allowCreate, bool onlyOneCheckConnection, DbConnectionOptions userOptions, out DbConnectionInternal connection) {
  983. DbConnectionInternal obj = null;
  984. SysTx.Transaction transaction = null;
  985. #if !MOBILE
  986. PerformanceCounters.SoftConnectsPerSecond.Increment();
  987. #endif
  988. Bid.PoolerTrace("<prov.DbConnectionPool.GetConnection|RES|CPOOL> %d#, Getting connection.\n", ObjectID);
  989. // If automatic transaction enlistment is required, then we try to
  990. // get the connection from the transacted connection pool first.
  991. if (HasTransactionAffinity) {
  992. obj = GetFromTransactedPool(out transaction);
  993. }
  994. if (null == obj) {
  995. Interlocked.Increment(ref _waitCount);
  996. uint waitHandleCount = allowCreate ? 3u : 2u;
  997. do {
  998. int waitResult = BOGUS_HANDLE;
  999. int releaseSemaphoreResult = 0;
  1000. bool mustRelease = false;
  1001. int waitForMultipleObjectsExHR = 0;
  1002. RuntimeHelpers.PrepareConstrainedRegions();
  1003. try {
  1004. _waitHandles.DangerousAddRef(ref mustRelease);
  1005. // We absolutely must have the value of waitResult set,
  1006. // or we may leak the mutex in async abort cases.
  1007. RuntimeHelpers.PrepareConstrainedRegions();
  1008. try {
  1009. Debug.Assert(2 == waitHandleCount || 3 == waitHandleCount, "unexpected waithandle count");
  1010. }
  1011. finally {
  1012. waitResult = SafeNativeMethods.WaitForMultipleObjectsEx(waitHandleCount, _waitHandles.DangerousGetHandle(), false, waitForMultipleObjectsTimeout, false);
  1013. #if !FULL_AOT_RUNTIME
  1014. // VSTFDEVDIV 479551 - call GetHRForLastWin32Error immediately after after the native call
  1015. if (waitResult == WAIT_FAILED) {
  1016. waitForMultipleObjectsExHR = Marshal.GetHRForLastWin32Error();
  1017. }
  1018. #endif
  1019. }
  1020. // From the WaitAny docs: "If more than one object became signaled during
  1021. // the call, this is the array index of the signaled object with the
  1022. // smallest index value of all the signaled objects." This is important
  1023. // so that the free object signal will be returned before a creation
  1024. // signal.
  1025. switch (waitResult) {
  1026. case WAIT_TIMEOUT:
  1027. Bid.PoolerTrace("<prov.DbConnectionPool.GetConnection|RES|CPOOL> %d#, Wait timed out.\n", ObjectID);
  1028. Interlocked.Decrement(ref _waitCount);
  1029. connection = null;
  1030. return false;
  1031. case ERROR_HANDLE:
  1032. // Throw the error that PoolCreateRequest stashed.
  1033. Bid.PoolerTrace("<prov.DbConnectionPool.GetConnection|RES|CPOOL> %d#, Errors are set.\n", ObjectID);
  1034. Interlocked.Decrement(ref _waitCount);
  1035. throw TryCloneCachedException();
  1036. case CREATION_HANDLE:
  1037. Bid.PoolerTrace("<prov.DbConnectionPool.GetConnection|RES|CPOOL> %d#, Creating new connection.\n", ObjectID);
  1038. try {
  1039. obj = UserCreateRequest(owningObject, userOptions);
  1040. }
  1041. catch {
  1042. if (null == obj) {
  1043. Interlocked.Decrement(ref _waitCount);
  1044. }
  1045. throw;
  1046. }
  1047. finally {
  1048. // SQLBUDT #386664 - ensure that we release this waiter, regardless
  1049. // of any exceptions that may be thrown.
  1050. if (null != obj) {
  1051. Interlocked.Decrement(ref _waitCount);
  1052. }
  1053. }
  1054. if (null == obj) {
  1055. // If we were not able to create an object, check to see if
  1056. // we reached MaxPoolSize. If so, we will no longer wait on
  1057. // the CreationHandle, but instead wait for a free object or
  1058. // the timeout.
  1059. //
  1060. if (Count >= MaxPoolSize && 0 != MaxPoolSize) {
  1061. if (!ReclaimEmancipatedObjects()) {
  1062. // modify handle array not to wait on creation mutex anymore
  1063. Debug.Assert(2 == CREATION_HANDLE, "creation handle changed value");
  1064. waitHandleCount = 2;
  1065. }
  1066. }
  1067. }
  1068. break;
  1069. case SEMAPHORE_HANDLE:
  1070. //
  1071. // guaranteed available inventory
  1072. //
  1073. Interlocked.Decrement(ref _waitCount);
  1074. obj = GetFromGeneralPool();
  1075. if ((obj != null) && (!obj.IsConnectionAlive())) {
  1076. Bid.PoolerTrace("<prov.DbConnectionPool.GetConnection|RES|CPOOL> %d#, Connection %d#, found dead and removed.\n", ObjectID, obj.ObjectID);
  1077. DestroyObject(obj);
  1078. obj = null; // Setting to null in case creating a new object fails
  1079. if (onlyOneCheckConnection) {
  1080. if (_waitHandles.CreationSemaphore.WaitOne(unchecked((int)waitForMultipleObjectsTimeout))) {
  1081. RuntimeHelpers.PrepareConstrainedRegions();
  1082. try {
  1083. Bid.PoolerTrace("<prov.DbConnectionPool.GetConnection|RES|CPOOL> %d#, Creating new connection.\n", ObjectID);
  1084. obj = UserCreateRequest(owningObject, userOptions);
  1085. }
  1086. finally {
  1087. _waitHandles.CreationSemaphore.Release(1);
  1088. }
  1089. }
  1090. else {
  1091. // Timeout waiting for creation semaphore - return null
  1092. Bid.PoolerTrace("<prov.DbConnectionPool.GetConnection|RES|CPOOL> %d#, Wait timed out.\n", ObjectID);
  1093. connection = null;
  1094. return false;
  1095. }
  1096. }
  1097. }
  1098. break;
  1099. case WAIT_FAILED:
  1100. Debug.Assert(waitForMultipleObjectsExHR != 0, "WaitForMultipleObjectsEx failed but waitForMultipleObjectsExHR remained 0");
  1101. Bid.PoolerTrace("<prov.DbConnectionPool.GetConnection|RES|CPOOL> %d#, Wait failed.\n", ObjectID);
  1102. Interlocked.Decrement(ref _waitCount);
  1103. Marshal.ThrowExceptionForHR(waitForMultipleObjectsExHR);
  1104. goto default; // if ThrowExceptionForHR didn't throw for some reason
  1105. case (WAIT_ABANDONED+SEMAPHORE_HANDLE):
  1106. Bid.PoolerTrace("<prov.DbConnectionPool.GetConnection|RES|CPOOL> %d#, Semaphore handle abandonded.\n", ObjectID);
  1107. Interlocked.Decrement(ref _waitCount);
  1108. throw new AbandonedMutexException(SEMAPHORE_HANDLE,_waitHandles.PoolSemaphore);
  1109. case (WAIT_ABANDONED+ERROR_HANDLE):
  1110. Bid.PoolerTrace("<prov.DbConnectionPool.GetConnection|RES|CPOOL> %d#, Error handle abandonded.\n", ObjectID);
  1111. Interlocked.Decrement(ref _waitCount);
  1112. throw new AbandonedMutexException(ERROR_HANDLE,_waitHandles.ErrorEvent);
  1113. case (WAIT_ABANDONED+CREATION_HANDLE):
  1114. Bid.PoolerTrace("<prov.DbConnectionPool.GetConnection|RES|CPOOL> %d#, Creation handle abandoned.\n", ObjectID);
  1115. Interlocked.Decrement(ref _waitCount);
  1116. throw new AbandonedMutexException(CREATION_HANDLE,_waitHandles.CreationSemaphore);
  1117. default:
  1118. Bid.PoolerTrace("<prov.DbConnectionPool.GetConnection|RES|CPOOL> %d#, WaitForMultipleObjects=%d\n", ObjectID, waitResult);
  1119. Interlocked.Decrement(ref _waitCount);
  1120. throw ADP.InternalError(ADP.InternalErrorCode.UnexpectedWaitAnyResult);
  1121. }
  1122. }
  1123. finally {
  1124. if (CREATION_HANDLE == waitResult) {
  1125. int result = SafeNativeMethods.ReleaseSemaphore(_waitHandles.CreationHandle.DangerousGetHandle(), 1, IntPtr.Zero);
  1126. if (0 == result) { // failure case
  1127. #if !FULL_AOT_RUNTIME
  1128. releaseSemaphoreResult = Marshal.GetHRForLastWin32Error();
  1129. #endif
  1130. }
  1131. }
  1132. if (mustRelease) {
  1133. _waitHandles.DangerousRelease();
  1134. }
  1135. }
  1136. if (0 != releaseSemaphoreResult) {
  1137. Marshal.ThrowExceptionForHR(releaseSemaphoreResult); // will only throw if (hresult < 0)
  1138. }
  1139. } while (null == obj);
  1140. }
  1141. if (null != obj)
  1142. {
  1143. PrepareConnection(owningObject, obj, transaction);
  1144. }
  1145. connection = obj;
  1146. return true;
  1147. }
  1148. private void PrepareConnection(DbConnection owningObject, DbConnectionInternal obj, SysTx.Transaction transaction) {
  1149. lock (obj)
  1150. { // Protect against Clear and ReclaimEmancipatedObjects, which call IsEmancipated, which is affected by PrePush and PostPop
  1151. obj.PostPop(owningObject);
  1152. }
  1153. try
  1154. {
  1155. obj.ActivateConnection(transaction);
  1156. }
  1157. catch
  1158. {
  1159. // if Activate throws an exception
  1160. // put it back in the pool or have it properly disposed of
  1161. this.PutObject(obj, owningObject);
  1162. throw;
  1163. }
  1164. }
  1165. /// <summary>
  1166. /// Creates a new connection to replace an existing connection
  1167. /// </summary>
  1168. /// <param name="owningObject">Outer connection that currently owns <paramref name="oldConnection"/></param>
  1169. /// <param name="userOptions">Options used to create the new connection</param>
  1170. /// <param name="oldConnection">Inner connection that will be replaced</param>
  1171. /// <returns>A new inner connection that is attached to the <paramref name="owningObject"/></returns>
  1172. internal DbConnectionInternal ReplaceConnection(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection) {
  1173. #if !MOBILE
  1174. PerformanceCounters.SoftConnectsPerSecond.Increment();
  1175. #endif
  1176. Bid.PoolerTrace("<prov.DbConnectionPool.ReplaceConnection|RES|CPOOL> %d#, replacing connection.\n", ObjectID);
  1177. DbConnectionInternal newConnection = UserCreateRequest(owningObject, userOptions, oldConnection);
  1178. if (newConnection != null) {
  1179. PrepareConnection(owningObject, newConnection, oldConnection.EnlistedTransaction);
  1180. oldConnection.PrepareForReplaceConnection();
  1181. oldConnection.DeactivateConnection();
  1182. oldConnection.Dispose();
  1183. }
  1184. return newConnection;
  1185. }
  1186. private DbConnectionInternal GetFromGeneralPool() {
  1187. DbConnectionInternal obj = null;
  1188. if (!_stackNew.TryPop(out obj)) {
  1189. if (!_stackOld.TryPop(out obj)) {
  1190. obj = null;
  1191. }
  1192. else {
  1193. Debug.Assert(obj != null, "null connection is not expected");
  1194. }
  1195. }
  1196. else {
  1197. Debug.Assert(obj != null, "null connection is not expected");
  1198. }
  1199. // SQLBUDT #356870 -- When another thread is clearing this pool,
  1200. // it will remove all connections in this pool which causes the
  1201. // following assert to fire, which really mucks up stress against
  1202. // checked bits. The assert is benign, so we're commenting it out.
  1203. //Debug.Assert(obj != null, "GetFromGeneralPool called with nothing in the pool!");
  1204. if (null != obj) {
  1205. Bid.PoolerTrace("<prov.DbConnectionPool.GetFromGeneralPool|RES|CPOOL> %d#, Connection %d#, Popped from general pool.\n", ObjectID, obj.ObjectID);
  1206. #if !MOBILE
  1207. PerformanceCounters.NumberOfFreeConnections.Decrement();
  1208. #endif
  1209. }
  1210. return(obj);
  1211. }
  1212. private DbConnectionInternal GetFromTransactedPool(out SysTx.Transaction transaction) {
  1213. transaction = ADP.GetCurrentTransaction();
  1214. DbConnectionInternal obj = null;
  1215. if (null != transaction && null != _transactedConnectionPool) {
  1216. obj = _transactedConnectionPool.GetTransactedObject(transaction);
  1217. if (null != obj) {
  1218. Bid.PoolerTrace("<prov.DbConnectionPool.GetFromTransactedPool|RES|CPOOL> %d#, Connection %d#, Popped from transacted pool.\n", ObjectID, obj.ObjectID);
  1219. #if !MOBILE
  1220. PerformanceCounters.NumberOfFreeConnections.Decrement();
  1221. #endif
  1222. if (obj.IsTransactionRoot) {
  1223. try {
  1224. obj.IsConnectionAlive(true);
  1225. }
  1226. catch {
  1227. Bid.PoolerTrace("<prov.DbConnectionPool.GetFromTransactedPool|RES|CPOOL> %d#, Connection %d#, found dead and removed.\n", ObjectID, obj.ObjectID);
  1228. DestroyObject(obj);
  1229. throw;
  1230. }
  1231. }
  1232. else if (!obj.IsConnectionAlive()) {
  1233. Bid.PoolerTrace("<prov.DbConnectionPool.GetFromTransactedPool|RES|CPOOL> %d#, Connection %d#, found dead and removed.\n", ObjectID, obj.ObjectID);
  1234. DestroyObject(obj);
  1235. obj = null;
  1236. }
  1237. }
  1238. }
  1239. return obj;
  1240. }
  1241. [ResourceExposure(ResourceScope.None)] // SxS: this method does not expose resources
  1242. [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
  1243. private void PoolCreateRequest(object state) {
  1244. // called by pooler to ensure pool requests are currently being satisfied -
  1245. // creation mutex has not been obtained
  1246. IntPtr hscp;
  1247. Bid.PoolerScopeEnter(out hscp, "<prov.DbConnectionPool.PoolCreateRequest|RES|INFO|CPOOL> %d#\n", ObjectID);
  1248. try {
  1249. if (State.Running == _state) {
  1250. // in case WaitForPendingOpen ever failed with no subsequent OpenAsync calls,
  1251. // start it back up again
  1252. if (!_pendingOpens.IsEmpty && _pendingOpensWaiting == 0) {
  1253. Thread waitOpenThread = new Thread(WaitForPendingOpen);
  1254. waitOpenThread.IsBackground = true;
  1255. waitOpenThread.Start();
  1256. }
  1257. // Before creating any new objects, reclaim any released objects that were
  1258. // not closed.
  1259. ReclaimEmancipatedObjects();
  1260. if (!ErrorOccurred) {
  1261. if (NeedToReplenish) {
  1262. // Check to see if pool was created using integrated security and if so, make
  1263. // sure the identity of current user matches that of user that created pool.
  1264. // If it doesn't match, do not create any objects on the ThreadPool thread,
  1265. // since either Open will fail or we will open a object for this pool that does
  1266. // not belong in this pool. The side effect of this is that if using integrated
  1267. // security min pool size cannot be guaranteed.
  1268. if (UsingIntegrateSecurity && !_identity.Equals(DbConnectionPoolIdentity.GetCurrent())) {
  1269. return;
  1270. }
  1271. bool mustRelease = false;
  1272. int waitResult = BOGUS_HANDLE;
  1273. uint timeout = (uint)CreationTimeout;
  1274. RuntimeHelpers.PrepareConstrainedRegions();
  1275. try {
  1276. _waitHandles.DangerousAddRef(ref mustRelease);
  1277. // Obtain creation mutex so we're the only one creating objects
  1278. // and we must have the wait result
  1279. RuntimeHelpers.PrepareConstrainedRegions();
  1280. try { } finally {
  1281. waitResult = SafeNativeMethods.WaitForSingleObjectEx(_waitHandles.CreationHandle.DangerousGetHandle(), timeout, false);
  1282. }
  1283. if (WAIT_OBJECT_0 == waitResult) {
  1284. DbConnectionInternal newObj;
  1285. // Check ErrorOccurred again after obtaining mutex
  1286. if (!ErrorOccurred) {
  1287. while (NeedToReplenish) {
  1288. // Don't specify any user options because there is no outer connection associated with the new connection
  1289. newObj = CreateObject(owningObject: null, userOptions: null, oldConnection: null);
  1290. // We do not need to check error flag here, since we know if
  1291. // CreateObject returned null, we are in error case.
  1292. if (null != newObj) {
  1293. PutNewObject(newObj);
  1294. }
  1295. else {
  1296. break;
  1297. }
  1298. }
  1299. }
  1300. }
  1301. else if (WAIT_TIMEOUT == waitResult) {
  1302. // do not wait forever and potential block this worker thread
  1303. // instead wait for a period of time and just requeue to try again
  1304. QueuePoolCreateRequest();
  1305. }
  1306. else {
  1307. // trace waitResult and ignore the failure
  1308. Bid.PoolerTrace("<prov.DbConnectionPool.PoolCreateRequest|RES|CPOOL> %d#, PoolCreateRequest called WaitForSingleObject failed %d", ObjectID, waitResult);
  1309. }
  1310. }
  1311. catch (Exception e) {
  1312. //
  1313. if (!ADP.IsCatchableExceptionType(e)) {
  1314. throw;
  1315. }
  1316. // Now that CreateObject can throw, we need to catch the exception and discard it.
  1317. // There is no further action we can take beyond tracing. The error will be
  1318. // thrown to the user the next time they request a connection.
  1319. Bid.PoolerTrace("<prov.DbConnectionPool.PoolCreateRequest|RES|CPOOL> %d#, PoolCreateRequest called CreateConnection which threw an exception: %ls", ObjectID, e);
  1320. }
  1321. finally {
  1322. if (WAIT_OBJECT_0 == waitResult) {
  1323. // reuse waitResult and ignore its value
  1324. waitResult = SafeNativeMethods.ReleaseSemaphore(_waitHandles.CreationHandle.DangerousGetHandle(), 1, IntPtr.Zero);
  1325. }
  1326. if (mustRelease) {
  1327. _waitHandles.DangerousRelease();
  1328. }
  1329. }
  1330. }
  1331. }
  1332. }
  1333. }
  1334. finally {
  1335. Bid.ScopeLeave(ref hscp);
  1336. }
  1337. }
  1338. internal void PutNewObject(DbConnectionInternal obj) {
  1339. Debug.Assert(null != obj, "why are we adding a null object to the pool?");
  1340. // VSTFDEVDIV 742887 - When another thread is clearing this pool, it
  1341. // will set _cannotBePooled for all connections in this pool without prejudice which
  1342. // causes the following assert to fire, which really mucks up stress
  1343. // against checked bits.
  1344. // Debug.Assert(obj.CanBePooled, "non-poolable object in pool");
  1345. Bid.PoolerTrace("<prov.DbConnectionPool.PutNewObject|RES|CPOOL> %d#, Connection %d#, Pushing to general pool.\n", ObjectID, obj.ObjectID);
  1346. _stackNew.Push(obj);
  1347. _waitHandles.PoolSemaphore.Release(1);
  1348. #if !MOBILE
  1349. PerformanceCounters.NumberOfFreeConnections.Increment();
  1350. #endif
  1351. }
  1352. internal void PutObject(DbConnectionInternal obj, object owningObject) {
  1353. Debug.Assert(null != obj, "null obj?");
  1354. #if !MOBILE
  1355. PerformanceCounters.SoftDisconnectsPerSecond.Increment();
  1356. #endif
  1357. // Once a connection is closing (which is the state that we're in at
  1358. // this point in time) you cannot delegate a transaction to or enlist
  1359. // a transaction in it, so we can correctly presume that if there was
  1360. // not a delegated or enlisted transaction to start with, that there
  1361. // will not be a delegated or enlisted transaction once we leave the
  1362. // lock.
  1363. lock (obj) {
  1364. // Calling PrePush prevents the object from being reclaimed
  1365. // once we leave the lock, because it sets _pooledCount such
  1366. // that it won't appear to be out of the pool. What that
  1367. // means, is that we're now responsible for this connection:
  1368. // it won't get reclaimed if we drop the ball somewhere.
  1369. obj.PrePush(owningObject);
  1370. //
  1371. }
  1372. DeactivateObject(obj);
  1373. }
  1374. internal void PutObjectFromTransactedPool(DbConnectionInternal obj) {
  1375. Debug.Assert(null != obj, "null pooledObject?");
  1376. Debug.Assert(obj.EnlistedTransaction == null, "pooledObject is still enlisted?");
  1377. // called by the transacted connection pool , once it's removed the
  1378. // connection from it's list. We put the connection back in general
  1379. // circulation.
  1380. // NOTE: there is no locking required here because if we're in this
  1381. // method, we can safely presume that the caller is the only person
  1382. // that is using the connection, and that all pre-push logic has been
  1383. // done and all transactions are ended.
  1384. Bid.PoolerTrace("<prov.DbConnectionPool.PutObjectFromTransactedPool|RES|CPOOL> %d#, Connection %d#, Transaction has ended.\n", ObjectID, obj.ObjectID);
  1385. if (_state == State.Running && obj.CanBePooled) {
  1386. PutNewObject(obj);
  1387. }
  1388. else {
  1389. DestroyObject(obj);
  1390. QueuePoolCreateRequest();
  1391. }
  1392. }
  1393. private void QueuePoolCreateRequest() {
  1394. if (State.Running == _state) {
  1395. // Make sure we're at quota by posting a callback to the threadpool.
  1396. ThreadPool.QueueUserWorkItem(_poolCreateRequest);
  1397. }
  1398. }
  1399. private bool ReclaimEmancipatedObjects() {
  1400. bool emancipatedObjectFound = false;
  1401. Bid.PoolerTrace("<prov.DbConnectionPool.ReclaimEmancipatedObjects|RES|CPOOL> %d#\n", ObjectID);
  1402. List<DbConnectionInternal> reclaimedObjects = new List<DbConnectionInternal>();
  1403. int count;
  1404. lock(_objectList) {
  1405. count = _objectList.Count;
  1406. for (int i = 0; i < count; ++i) {
  1407. DbConnectionInternal obj = _objectList[i];
  1408. if (null != obj) {
  1409. bool locked = false;
  1410. try {
  1411. Monitor.TryEnter(obj, ref locked);
  1412. if (locked) { // avoid race condition with PrePush/PostPop and IsEmancipated
  1413. if (obj.IsEmancipated) {
  1414. // Inside the lock, we want to do as little
  1415. // as possible, so we simply mark the object
  1416. // as being in the pool, but hand it off to
  1417. // an out of pool list to be deactivated,
  1418. // etc.
  1419. obj.PrePush(null);
  1420. reclaimedObjects.Add(obj);
  1421. }
  1422. }
  1423. }
  1424. finally {
  1425. if (locked)
  1426. Monitor.Exit(obj);
  1427. }
  1428. }
  1429. }
  1430. }
  1431. // NOTE: we don't want to call DeactivateObject while we're locked,
  1432. // because it can make roundtrips to the server and this will block
  1433. // object creation in the pooler. Instead, we queue things we need
  1434. // to do up, and process them outside the lock.
  1435. count = reclaimedObjects.Count;
  1436. for (int i = 0; i < count; ++i) {
  1437. DbConnectionInternal obj = reclaimedObjects[i];
  1438. Bid.PoolerTrace("<prov.DbConnectionPool.ReclaimEmancipatedObjects|RES|CPOOL> %d#, Connection %d#, Reclaiming.\n", ObjectID, obj.ObjectID);
  1439. #if !MOBILE
  1440. PerformanceCounters.NumberOfReclaimedConnections.Increment();
  1441. #endif
  1442. emancipatedObjectFound = true;
  1443. obj.DetachCurrentTransactionIfEnded();
  1444. DeactivateObject(obj);
  1445. }
  1446. return emancipatedObjectFound;
  1447. }
  1448. internal void Startup() {
  1449. Bid.PoolerTrace("<prov.DbConnectionPool.Startup|RES|INFO|CPOOL> %d#, CleanupWait=%d\n", ObjectID, _cleanupWait);
  1450. _cleanupTimer = CreateCleanupTimer();
  1451. if (NeedToReplenish) {
  1452. QueuePoolCreateRequest();
  1453. }
  1454. }
  1455. internal void Shutdown() {
  1456. Bid.PoolerTrace("<prov.DbConnectionPool.Shutdown|RES|INFO|CPOOL> %d#\n", ObjectID);
  1457. _state = State.ShuttingDown;
  1458. // deactivate timer callbacks
  1459. Timer t = _cleanupTimer;
  1460. _cleanupTimer = null;
  1461. if (null != t) {
  1462. t.Dispose();
  1463. }
  1464. }
  1465. // TransactionEnded merely provides the plumbing for DbConnectionInternal to access the transacted pool
  1466. // that is implemented inside DbConnectionPool. This method's counterpart (PutTransactedObject) should
  1467. // only be called from DbConnectionPool.DeactivateObject and thus the plumbing to provide access to
  1468. // other objects is unnecessary (hence the asymmetry of Ended but no Begin)
  1469. internal void TransactionEnded(SysTx.Transaction transaction, DbConnectionInternal transactedObject) {
  1470. Debug.Assert(null != transaction, "null transaction?");
  1471. Debug.Assert(null != transactedObject, "null transactedObject?");
  1472. // Note: connection may still be associated with transaction due to Explicit Unbinding requirement.
  1473. Bid.PoolerTrace("<prov.DbConnectionPool.TransactionEnded|RES|CPOOL> %d#, Transaction %d#, Connection %d#, Transaction Completed\n", ObjectID, transaction.GetHashCode(), transactedObject.ObjectID);
  1474. // called by the internal connection when it get's told that the
  1475. // transaction is completed. We tell the transacted pool to remove
  1476. // the connection from it's list, then we put the connection back in
  1477. // general circulation.
  1478. TransactedConnectionPool transactedConnectionPool = _transactedConnectionPool;
  1479. if (null != transactedConnectionPool) {
  1480. transactedConnectionPool.TransactionEnded(transaction, transactedObject);
  1481. }
  1482. }
  1483. private DbConnectionInternal UserCreateRequest(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection = null) {
  1484. // called by user when they were not able to obtain a free object but
  1485. // instead obtained creation mutex
  1486. DbConnectionInternal obj = null;
  1487. if (ErrorOccurred) {
  1488. throw TryCloneCachedException();
  1489. }
  1490. else {
  1491. if ((oldConnection != null) || (Count < MaxPoolSize) || (0 == MaxPoolSize)) {
  1492. // If we have an odd number of total objects, reclaim any dead objects.
  1493. // If we did not find any objects to reclaim, create a new one.
  1494. //
  1495. if ((oldConnection != null) || (Count & 0x1) == 0x1 || !ReclaimEmancipatedObjects())
  1496. obj = CreateObject(owningObject, userOptions, oldConnection);
  1497. }
  1498. return obj;
  1499. }
  1500. }
  1501. }
  1502. }