DbConnectionPool.cs 83 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778
  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. private State _state;
  355. private readonly ConcurrentStack<DbConnectionInternal> _stackOld = new ConcurrentStack<DbConnectionInternal>();
  356. private readonly ConcurrentStack<DbConnectionInternal> _stackNew = new ConcurrentStack<DbConnectionInternal>();
  357. private readonly ConcurrentQueue<PendingGetConnection> _pendingOpens = new ConcurrentQueue<PendingGetConnection>();
  358. private int _pendingOpensWaiting = 0;
  359. private readonly WaitCallback _poolCreateRequest;
  360. private int _waitCount;
  361. private readonly PoolWaitHandles _waitHandles;
  362. private Exception _resError;
  363. private volatile bool _errorOccurred;
  364. private int _errorWait;
  365. private Timer _errorTimer;
  366. private Timer _cleanupTimer;
  367. private readonly TransactedConnectionPool _transactedConnectionPool;
  368. private readonly List<DbConnectionInternal> _objectList;
  369. private int _totalObjects;
  370. private static int _objectTypeCount; // Bid counter
  371. internal readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount);
  372. // only created by DbConnectionPoolGroup.GetConnectionPool
  373. internal DbConnectionPool(
  374. DbConnectionFactory connectionFactory,
  375. DbConnectionPoolGroup connectionPoolGroup,
  376. DbConnectionPoolIdentity identity,
  377. DbConnectionPoolProviderInfo connectionPoolProviderInfo ) {
  378. Debug.Assert(ADP.IsWindowsNT, "Attempting to construct a connection pool on Win9x?");
  379. Debug.Assert(null != connectionPoolGroup, "null connectionPoolGroup");
  380. if ((null != identity) && identity.IsRestricted) {
  381. throw ADP.InternalError(ADP.InternalErrorCode.AttemptingToPoolOnRestrictedToken);
  382. }
  383. _state= State.Initializing;
  384. lock(_random) { // Random.Next is not thread-safe
  385. _cleanupWait = _random.Next(12, 24)*10*1000; // 2-4 minutes in 10 sec intervals, WebData 103603
  386. }
  387. _connectionFactory = connectionFactory;
  388. _connectionPoolGroup = connectionPoolGroup;
  389. _connectionPoolGroupOptions = connectionPoolGroup.PoolGroupOptions;
  390. _connectionPoolProviderInfo = connectionPoolProviderInfo;
  391. _identity = identity;
  392. _waitHandles = new PoolWaitHandles();
  393. _errorWait = ERROR_WAIT_DEFAULT;
  394. _errorTimer = null; // No error yet.
  395. _objectList = new List<DbConnectionInternal>(MaxPoolSize);
  396. if(ADP.IsPlatformNT5) {
  397. _transactedConnectionPool = new TransactedConnectionPool(this);
  398. }
  399. _poolCreateRequest = new WaitCallback(PoolCreateRequest); // used by CleanupCallback
  400. _state = State.Running;
  401. Bid.PoolerTrace("<prov.DbConnectionPool.DbConnectionPool|RES|CPOOL> %d#, Constructed.\n", ObjectID);
  402. //_cleanupTimer & QueuePoolCreateRequest is delayed until DbConnectionPoolGroup calls
  403. // StartBackgroundCallbacks after pool is actually in the collection
  404. }
  405. private int CreationTimeout {
  406. get { return PoolGroupOptions.CreationTimeout; }
  407. }
  408. internal int Count {
  409. get { return _totalObjects; }
  410. }
  411. internal DbConnectionFactory ConnectionFactory {
  412. get { return _connectionFactory; }
  413. }
  414. internal bool ErrorOccurred {
  415. get { return _errorOccurred; }
  416. }
  417. private bool HasTransactionAffinity {
  418. get { return PoolGroupOptions.HasTransactionAffinity; }
  419. }
  420. internal TimeSpan LoadBalanceTimeout {
  421. get { return PoolGroupOptions.LoadBalanceTimeout; }
  422. }
  423. private bool NeedToReplenish {
  424. get {
  425. if (State.Running != _state) // SQL BU DT 364595 - don't allow connection create when not running.
  426. return false;
  427. int totalObjects = Count;
  428. if (totalObjects >= MaxPoolSize)
  429. return false;
  430. if (totalObjects < MinPoolSize)
  431. return true;
  432. int freeObjects = (_stackNew.Count + _stackOld.Count);
  433. int waitingRequests = _waitCount;
  434. bool needToReplenish = (freeObjects < waitingRequests) || ((freeObjects == waitingRequests) && (totalObjects > 1));
  435. return needToReplenish;
  436. }
  437. }
  438. internal DbConnectionPoolIdentity Identity {
  439. get { return _identity; }
  440. }
  441. internal bool IsRunning {
  442. get { return State.Running == _state; }
  443. }
  444. private int MaxPoolSize {
  445. get { return PoolGroupOptions.MaxPoolSize; }
  446. }
  447. private int MinPoolSize {
  448. get { return PoolGroupOptions.MinPoolSize; }
  449. }
  450. internal int ObjectID {
  451. get {
  452. return _objectID;
  453. }
  454. }
  455. internal DbConnectionPoolCounters PerformanceCounters {
  456. get { return _connectionFactory.PerformanceCounters; }
  457. }
  458. internal DbConnectionPoolGroup PoolGroup {
  459. get { return _connectionPoolGroup; }
  460. }
  461. internal DbConnectionPoolGroupOptions PoolGroupOptions {
  462. get { return _connectionPoolGroupOptions; }
  463. }
  464. internal DbConnectionPoolProviderInfo ProviderInfo {
  465. get { return _connectionPoolProviderInfo; }
  466. }
  467. internal bool UseLoadBalancing {
  468. get { return PoolGroupOptions.UseLoadBalancing; }
  469. }
  470. private bool UsingIntegrateSecurity {
  471. get { return (null != _identity && DbConnectionPoolIdentity.NoIdentity != _identity); }
  472. }
  473. private void CleanupCallback(Object state) {
  474. // Called when the cleanup-timer ticks over.
  475. // This is the automatic prunning method. Every period, we will
  476. // perform a two-step process:
  477. //
  478. // First, for each free object above MinPoolSize, we will obtain a
  479. // semaphore representing one object and destroy one from old stack.
  480. // We will continue this until we either reach MinPoolSize, we are
  481. // unable to obtain a free object, or we have exhausted all the
  482. // objects on the old stack.
  483. //
  484. // Second we move all free objects on the new stack to the old stack.
  485. // So, every period the objects on the old stack are destroyed and
  486. // the objects on the new stack are pushed to the old stack. All
  487. // objects that are currently out and in use are not on either stack.
  488. //
  489. // With this logic, objects are pruned from the pool if unused for
  490. // at least one period but not more than two periods.
  491. Bid.PoolerTrace("<prov.DbConnectionPool.CleanupCallback|RES|INFO|CPOOL> %d#\n", ObjectID);
  492. // Destroy free objects that put us above MinPoolSize from old stack.
  493. while(Count > MinPoolSize) { // While above MinPoolSize...
  494. if (_waitHandles.PoolSemaphore.WaitOne(0, false) /* != WAIT_TIMEOUT */) {
  495. // We obtained a objects from the semaphore.
  496. DbConnectionInternal obj;
  497. if (_stackOld.TryPop(out obj)) {
  498. Debug.Assert(obj != null, "null connection is not expected");
  499. // If we obtained one from the old stack, destroy it.
  500. #if !MOBILE
  501. PerformanceCounters.NumberOfFreeConnections.Decrement();
  502. #endif
  503. // Transaction roots must survive even aging out (TxEnd event will clean them up).
  504. bool shouldDestroy = true;
  505. lock (obj) { // Lock to prevent race condition window between IsTransactionRoot and shouldDestroy assignment
  506. if (obj.IsTransactionRoot) {
  507. shouldDestroy = false;
  508. }
  509. }
  510. // !!!!!!!!!! WARNING !!!!!!!!!!!!!
  511. // ONLY touch obj after lock release if shouldDestroy is false!!! Otherwise, it may be destroyed
  512. // by transaction-end thread!
  513. // Note that there is a minor race condition between this task and the transaction end event, if the latter runs
  514. // between the lock above and the SetInStasis call below. The reslult is that the stasis counter may be
  515. // incremented without a corresponding decrement (the transaction end task is normally expected
  516. // to decrement, but will only do so if the stasis flag is set when it runs). I've minimized the size
  517. // of the window, but we aren't totally eliminating it due to SetInStasis needing to do bid tracing, which
  518. // we don't want to do under this lock, if possible. It should be possible to eliminate this race condition with
  519. // more substantial re-architecture of the pool, but we don't have the time to do that work for the current release.
  520. if (shouldDestroy) {
  521. DestroyObject(obj);
  522. }
  523. else {
  524. obj.SetInStasis();
  525. }
  526. }
  527. else {
  528. // Else we exhausted the old stack (the object the
  529. // semaphore represents is on the new stack), so break.
  530. _waitHandles.PoolSemaphore.Release(1);
  531. break;
  532. }
  533. }
  534. else {
  535. break;
  536. }
  537. }
  538. // Push to the old-stack. For each free object, move object from
  539. // new stack to old stack.
  540. if(_waitHandles.PoolSemaphore.WaitOne(0, false) /* != WAIT_TIMEOUT */) {
  541. for(;;) {
  542. DbConnectionInternal obj;
  543. if (!_stackNew.TryPop(out obj))
  544. break;
  545. Debug.Assert(obj != null, "null connection is not expected");
  546. Bid.PoolerTrace("<prov.DbConnectionPool.CleanupCallback|RES|INFO|CPOOL> %d#, ChangeStacks=%d#\n", ObjectID, obj.ObjectID);
  547. Debug.Assert(!obj.IsEmancipated, "pooled object not in pool");
  548. Debug.Assert(obj.CanBePooled, "pooled object is not poolable");
  549. _stackOld.Push(obj);
  550. }
  551. _waitHandles.PoolSemaphore.Release(1);
  552. }
  553. // Queue up a request to bring us up to MinPoolSize
  554. QueuePoolCreateRequest();
  555. }
  556. internal void Clear() {
  557. Bid.PoolerTrace("<prov.DbConnectionPool.Clear|RES|CPOOL> %d#, Clearing.\n", ObjectID);
  558. DbConnectionInternal obj;
  559. // First, quickly doom everything.
  560. lock(_objectList) {
  561. int count = _objectList.Count;
  562. for (int i = 0; i < count; ++i) {
  563. obj = _objectList[i];
  564. if (null != obj) {
  565. obj.DoNotPoolThisConnection();
  566. }
  567. }
  568. }
  569. // Second, dispose of all the free connections.
  570. while (_stackNew.TryPop(out obj)) {
  571. Debug.Assert(obj != null, "null connection is not expected");
  572. #if !MOBILE
  573. PerformanceCounters.NumberOfFreeConnections.Decrement();
  574. #endif
  575. DestroyObject(obj);
  576. }
  577. while (_stackOld.TryPop(out obj)) {
  578. Debug.Assert(obj != null, "null connection is not expected");
  579. #if !MOBILE
  580. PerformanceCounters.NumberOfFreeConnections.Decrement();
  581. #endif
  582. DestroyObject(obj);
  583. }
  584. // Finally, reclaim everything that's emancipated (which, because
  585. // it's been doomed, will cause it to be disposed of as well)
  586. ReclaimEmancipatedObjects();
  587. Bid.PoolerTrace("<prov.DbConnectionPool.Clear|RES|CPOOL> %d#, Cleared.\n", ObjectID);
  588. }
  589. private Timer CreateCleanupTimer() {
  590. return (new Timer(new TimerCallback(this.CleanupCallback), null, _cleanupWait, _cleanupWait));
  591. }
  592. private DbConnectionInternal CreateObject(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection) {
  593. DbConnectionInternal newObj = null;
  594. try {
  595. newObj = _connectionFactory.CreatePooledConnection(this, owningObject, _connectionPoolGroup.ConnectionOptions, _connectionPoolGroup.PoolKey, userOptions);
  596. if (null == newObj) {
  597. throw ADP.InternalError(ADP.InternalErrorCode.CreateObjectReturnedNull); // CreateObject succeeded, but null object
  598. }
  599. if (!newObj.CanBePooled) {
  600. throw ADP.InternalError(ADP.InternalErrorCode.NewObjectCannotBePooled); // CreateObject succeeded, but non-poolable object
  601. }
  602. newObj.PrePush(null);
  603. lock (_objectList) {
  604. if ((oldConnection != null) && (oldConnection.Pool == this)) {
  605. _objectList.Remove(oldConnection);
  606. }
  607. _objectList.Add(newObj);
  608. _totalObjects = _objectList.Count;
  609. #if !MOBILE
  610. PerformanceCounters.NumberOfPooledConnections.Increment(); //
  611. #endif
  612. }
  613. // If the old connection belonged to another pool, we need to remove it from that
  614. if (oldConnection != null) {
  615. var oldConnectionPool = oldConnection.Pool;
  616. if (oldConnectionPool != null && oldConnectionPool != this) {
  617. Debug.Assert(oldConnectionPool._state == State.ShuttingDown, "Old connections pool should be shutting down");
  618. lock (oldConnectionPool._objectList) {
  619. oldConnectionPool._objectList.Remove(oldConnection);
  620. oldConnectionPool._totalObjects = oldConnectionPool._objectList.Count;
  621. }
  622. }
  623. }
  624. Bid.PoolerTrace("<prov.DbConnectionPool.CreateObject|RES|CPOOL> %d#, Connection %d#, Added to pool.\n", ObjectID, newObj.ObjectID);
  625. // Reset the error wait:
  626. _errorWait = ERROR_WAIT_DEFAULT;
  627. }
  628. catch(Exception e) {
  629. //
  630. if (!ADP.IsCatchableExceptionType(e)) {
  631. throw;
  632. }
  633. ADP.TraceExceptionForCapture(e);
  634. newObj = null; // set to null, so we do not return bad new object
  635. // Failed to create instance
  636. _resError = e;
  637. // VSTFDEVDIV 479561: Make sure the timer starts even if ThreadAbort occurs after setting the ErrorEvent.
  638. // timer allocation has to be done out of CER block
  639. Timer t = new Timer(new TimerCallback(this.ErrorCallback), null, Timeout.Infinite, Timeout.Infinite);
  640. bool timerIsNotDisposed;
  641. RuntimeHelpers.PrepareConstrainedRegions();
  642. try{} finally {
  643. _waitHandles.ErrorEvent.Set();
  644. _errorOccurred = true;
  645. // Enable the timer.
  646. // Note that the timer is created to allow periodic invocation. If ThreadAbort occurs in the middle of ErrorCallback,
  647. // the timer will restart. Otherwise, the timer callback (ErrorCallback) destroys the timer after resetting the error to avoid second callback.
  648. _errorTimer = t;
  649. timerIsNotDisposed = t.Change(_errorWait, _errorWait);
  650. }
  651. Debug.Assert(timerIsNotDisposed, "ErrorCallback timer has been disposed");
  652. if (30000 < _errorWait) {
  653. _errorWait = 60000;
  654. }
  655. else {
  656. _errorWait *= 2;
  657. }
  658. throw;
  659. }
  660. return newObj;
  661. }
  662. private void DeactivateObject(DbConnectionInternal obj)
  663. {
  664. Bid.PoolerTrace("<prov.DbConnectionPool.DeactivateObject|RES|CPOOL> %d#, Connection %d#, Deactivating.\n", ObjectID, obj.ObjectID);
  665. obj.DeactivateConnection(); // we presume this operation is safe outside of a lock...
  666. bool returnToGeneralPool = false;
  667. bool destroyObject = false;
  668. bool rootTxn = false;
  669. if ( obj.IsConnectionDoomed )
  670. {
  671. // the object is not fit for reuse -- just dispose of it.
  672. destroyObject = true;
  673. }
  674. else
  675. {
  676. // NOTE: constructor should ensure that current state cannot be State.Initializing, so it can only
  677. // be State.Running or State.ShuttingDown
  678. Debug.Assert ( _state == State.Running || _state == State.ShuttingDown );
  679. lock (obj)
  680. {
  681. // A connection with a delegated transaction cannot currently
  682. // be returned to a different customer until the transaction
  683. // actually completes, so we send it into Stasis -- the SysTx
  684. // transaction object will ensure that it is owned (not lost),
  685. // and it will be certain to put it back into the pool.
  686. if ( _state == State.ShuttingDown )
  687. {
  688. if ( obj.IsTransactionRoot )
  689. {
  690. // SQLHotfix# 50003503 - connections that are affiliated with a
  691. // root transaction and that also happen to be in a connection
  692. // pool that is being shutdown need to be put in stasis so that
  693. // the root transaction isn't effectively orphaned with no
  694. // means to promote itself to a full delegated transaction or
  695. // Commit or Rollback
  696. obj.SetInStasis();
  697. rootTxn = true;
  698. }
  699. else
  700. {
  701. // connection is being closed and the pool has been marked as shutting
  702. // down, so destroy this object.
  703. destroyObject = true;
  704. }
  705. }
  706. else
  707. {
  708. if ( obj.IsNonPoolableTransactionRoot )
  709. {
  710. obj.SetInStasis();
  711. rootTxn = true;
  712. }
  713. else if ( obj.CanBePooled )
  714. {
  715. // We must put this connection into the transacted pool
  716. // while inside a lock to prevent a race condition with
  717. // the transaction asyncronously completing on a second
  718. // thread.
  719. SysTx.Transaction transaction = obj.EnlistedTransaction;
  720. if (null != transaction)
  721. {
  722. // NOTE: we're not locking on _state, so it's possible that its
  723. // value could change between the conditional check and here.
  724. // Although perhaps not ideal, this is OK because the
  725. // DelegatedTransactionEnded event will clean up the
  726. // connection appropriately regardless of the pool state.
  727. Debug.Assert ( _transactedConnectionPool != null, "Transacted connection pool was not expected to be null.");
  728. _transactedConnectionPool.PutTransactedObject(transaction, obj);
  729. rootTxn = true;
  730. }
  731. else
  732. {
  733. // return to general pool
  734. returnToGeneralPool = true;
  735. }
  736. }
  737. else
  738. {
  739. if ( obj.IsTransactionRoot && !obj.IsConnectionDoomed )
  740. {
  741. // SQLHotfix# 50003503 - if the object cannot be pooled but is a transaction
  742. // root, then we must have hit one of two race conditions:
  743. // 1) PruneConnectionPoolGroups shutdown the pool and marked this connection
  744. // as non-poolable while we were processing within this lock
  745. // 2) The LoadBalancingTimeout expired on this connection and marked this
  746. // connection as DoNotPool.
  747. //
  748. // This connection needs to be put in stasis so that the root transaction isn't
  749. // effectively orphaned with no means to promote itself to a full delegated
  750. // transaction or Commit or Rollback
  751. obj.SetInStasis();
  752. rootTxn = true;
  753. }
  754. else
  755. {
  756. // object is not fit for reuse -- just dispose of it
  757. destroyObject = true;
  758. }
  759. }
  760. }
  761. }
  762. }
  763. if (returnToGeneralPool)
  764. {
  765. // Only push the connection into the general pool if we didn't
  766. // already push it onto the transacted pool, put it into stasis,
  767. // or want to destroy it.
  768. Debug.Assert ( destroyObject == false );
  769. PutNewObject(obj);
  770. }
  771. else if ( destroyObject )
  772. {
  773. // VSTFDEVDIV# 479556 - connections that have been marked as no longer
  774. // poolable (e.g. exceeded their connection lifetime) are not, in fact,
  775. // returned to the general pool
  776. DestroyObject(obj);
  777. QueuePoolCreateRequest();
  778. }
  779. //-------------------------------------------------------------------------------------
  780. // postcondition
  781. // ensure that the connection was processed
  782. Debug.Assert ( rootTxn == true || returnToGeneralPool == true || destroyObject == true );
  783. //
  784. }
  785. internal void DestroyObject(DbConnectionInternal obj) {
  786. // A connection with a delegated transaction cannot be disposed of
  787. // until the delegated transaction has actually completed. Instead,
  788. // we simply leave it alone; when the transaction completes, it will
  789. // come back through PutObjectFromTransactedPool, which will call us
  790. // again.
  791. if (obj.IsTxRootWaitingForTxEnd) {
  792. Bid.PoolerTrace("<prov.DbConnectionPool.DestroyObject|RES|CPOOL> %d#, Connection %d#, Has Delegated Transaction, waiting to Dispose.\n", ObjectID, obj.ObjectID);
  793. }
  794. else {
  795. Bid.PoolerTrace("<prov.DbConnectionPool.DestroyObject|RES|CPOOL> %d#, Connection %d#, Removing from pool.\n", ObjectID, obj.ObjectID);
  796. bool removed = false;
  797. lock (_objectList) {
  798. removed = _objectList.Remove(obj);
  799. Debug.Assert(removed, "attempt to DestroyObject not in list");
  800. _totalObjects = _objectList.Count;
  801. }
  802. if (removed) {
  803. Bid.PoolerTrace("<prov.DbConnectionPool.DestroyObject|RES|CPOOL> %d#, Connection %d#, Removed from pool.\n", ObjectID, obj.ObjectID);
  804. #if !MOBILE
  805. PerformanceCounters.NumberOfPooledConnections.Decrement();
  806. #endif
  807. }
  808. obj.Dispose();
  809. Bid.PoolerTrace("<prov.DbConnectionPool.DestroyObject|RES|CPOOL> %d#, Connection %d#, Disposed.\n", ObjectID, obj.ObjectID);
  810. #if !MOBILE
  811. PerformanceCounters.HardDisconnectsPerSecond.Increment();
  812. #endif
  813. }
  814. }
  815. private void ErrorCallback(Object state) {
  816. Bid.PoolerTrace("<prov.DbConnectionPool.ErrorCallback|RES|CPOOL> %d#, Resetting Error handling.\n", ObjectID);
  817. _errorOccurred = false;
  818. _waitHandles.ErrorEvent.Reset();
  819. // the error state is cleaned, destroy the timer to avoid periodic invocation
  820. Timer t = _errorTimer;
  821. _errorTimer = null;
  822. if (t != null) {
  823. t.Dispose(); // Cancel timer request.
  824. }
  825. }
  826. private Exception TryCloneCachedException()
  827. // Cached exception can be of any type, so is not always cloneable.
  828. // This functions clones SqlException
  829. // OleDb and Odbc connections are not passing throw this code
  830. {
  831. if (_resError==null)
  832. return null;
  833. if (_resError.GetType()==typeof(SqlClient.SqlException))
  834. return ((SqlClient.SqlException)_resError).InternalClone();
  835. return _resError;
  836. }
  837. void WaitForPendingOpen() {
  838. Debug.Assert(!Thread.CurrentThread.IsThreadPoolThread, "This thread may block for a long time. Threadpool threads should not be used.");
  839. PendingGetConnection next;
  840. do {
  841. bool started = false;
  842. RuntimeHelpers.PrepareConstrainedRegions();
  843. try {
  844. RuntimeHelpers.PrepareConstrainedRegions();
  845. try { }
  846. finally {
  847. started = Interlocked.CompareExchange(ref _pendingOpensWaiting, 1, 0) == 0;
  848. }
  849. if (!started) {
  850. return;
  851. }
  852. while (_pendingOpens.TryDequeue(out next)) {
  853. if (next.Completion.Task.IsCompleted) {
  854. continue;
  855. }
  856. uint delay;
  857. if (next.DueTime == Timeout.Infinite) {
  858. delay = unchecked((uint)Timeout.Infinite);
  859. }
  860. else {
  861. delay = (uint)Math.Max(ADP.TimerRemainingMilliseconds(next.DueTime), 0);
  862. }
  863. DbConnectionInternal connection = null;
  864. bool timeout = false;
  865. Exception caughtException = null;
  866. RuntimeHelpers.PrepareConstrainedRegions();
  867. try {
  868. #if DEBUG
  869. System.Data.SqlClient.TdsParser.ReliabilitySection tdsReliabilitySection = new System.Data.SqlClient.TdsParser.ReliabilitySection();
  870. RuntimeHelpers.PrepareConstrainedRegions();
  871. try {
  872. tdsReliabilitySection.Start();
  873. #else
  874. {
  875. #endif //DEBUG
  876. bool allowCreate = true;
  877. bool onlyOneCheckConnection = false;
  878. ADP.SetCurrentTransaction(next.Completion.Task.AsyncState as Transactions.Transaction);
  879. timeout = !TryGetConnection(next.Owner, delay, allowCreate, onlyOneCheckConnection, next.UserOptions, out connection);
  880. }
  881. #if DEBUG
  882. finally {
  883. tdsReliabilitySection.Stop();
  884. }
  885. #endif //DEBUG
  886. }
  887. catch (System.OutOfMemoryException) {
  888. if (connection != null) { connection.DoomThisConnection(); }
  889. throw;
  890. }
  891. catch (System.StackOverflowException) {
  892. if (connection != null) { connection.DoomThisConnection(); }
  893. throw;
  894. }
  895. catch (System.Threading.ThreadAbortException) {
  896. if (connection != null) { connection.DoomThisConnection(); }
  897. throw;
  898. }
  899. catch (Exception e) {
  900. caughtException = e;
  901. }
  902. if (caughtException != null) {
  903. next.Completion.TrySetException(caughtException);
  904. }
  905. else if (timeout) {
  906. next.Completion.TrySetException(ADP.ExceptionWithStackTrace(ADP.PooledOpenTimeout()));
  907. }
  908. else {
  909. Debug.Assert(connection != null, "connection should never be null in success case");
  910. if (!next.Completion.TrySetResult(connection)) {
  911. // if the completion was cancelled, lets try and get this connection back for the next try
  912. PutObject(connection, next.Owner);
  913. }
  914. }
  915. }
  916. }
  917. finally {
  918. if (started) {
  919. Interlocked.Exchange(ref _pendingOpensWaiting, 0);
  920. }
  921. }
  922. } while (_pendingOpens.TryPeek(out next));
  923. }
  924. internal bool TryGetConnection(DbConnection owningObject, TaskCompletionSource<DbConnectionInternal> retry, DbConnectionOptions userOptions, out DbConnectionInternal connection) {
  925. uint waitForMultipleObjectsTimeout = 0;
  926. bool allowCreate = false;
  927. if (retry == null) {
  928. waitForMultipleObjectsTimeout = (uint)CreationTimeout;
  929. // VSTFDEVDIV 445531: set the wait timeout to INFINITE (-1) if the SQL connection timeout is 0 (== infinite)
  930. if (waitForMultipleObjectsTimeout == 0)
  931. waitForMultipleObjectsTimeout = unchecked((uint)Timeout.Infinite);
  932. allowCreate = true;
  933. }
  934. if (_state != State.Running) {
  935. Bid.PoolerTrace("<prov.DbConnectionPool.GetConnection|RES|CPOOL> %d#, DbConnectionInternal State != Running.\n", ObjectID);
  936. connection = null;
  937. return true;
  938. }
  939. bool onlyOneCheckConnection = true;
  940. if (TryGetConnection(owningObject, waitForMultipleObjectsTimeout, allowCreate, onlyOneCheckConnection, userOptions, out connection)) {
  941. return true;
  942. }
  943. else if (retry == null) {
  944. // timed out on a [....] call
  945. return true;
  946. }
  947. var pendingGetConnection =
  948. new PendingGetConnection(
  949. CreationTimeout == 0 ? Timeout.Infinite : ADP.TimerCurrent() + ADP.TimerFromSeconds(CreationTimeout/1000),
  950. owningObject,
  951. retry,
  952. userOptions);
  953. _pendingOpens.Enqueue(pendingGetConnection);
  954. // it is better to StartNew too many times than not enough
  955. if (_pendingOpensWaiting == 0) {
  956. Thread waitOpenThread = new Thread(WaitForPendingOpen);
  957. waitOpenThread.IsBackground = true;
  958. waitOpenThread.Start();
  959. }
  960. connection = null;
  961. return false;
  962. }
  963. [SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods")] // copied from Triaged.cs
  964. [ResourceExposure(ResourceScope.None)] // SxS: this method does not expose resources
  965. [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
  966. private bool TryGetConnection(DbConnection owningObject, uint waitForMultipleObjectsTimeout, bool allowCreate, bool onlyOneCheckConnection, DbConnectionOptions userOptions, out DbConnectionInternal connection) {
  967. DbConnectionInternal obj = null;
  968. SysTx.Transaction transaction = null;
  969. #if !MOBILE
  970. PerformanceCounters.SoftConnectsPerSecond.Increment();
  971. #endif
  972. Bid.PoolerTrace("<prov.DbConnectionPool.GetConnection|RES|CPOOL> %d#, Getting connection.\n", ObjectID);
  973. // If automatic transaction enlistment is required, then we try to
  974. // get the connection from the transacted connection pool first.
  975. if (HasTransactionAffinity) {
  976. obj = GetFromTransactedPool(out transaction);
  977. }
  978. if (null == obj) {
  979. Interlocked.Increment(ref _waitCount);
  980. uint waitHandleCount = allowCreate ? 3u : 2u;
  981. do {
  982. int waitResult = BOGUS_HANDLE;
  983. int releaseSemaphoreResult = 0;
  984. bool mustRelease = false;
  985. int waitForMultipleObjectsExHR = 0;
  986. RuntimeHelpers.PrepareConstrainedRegions();
  987. try {
  988. _waitHandles.DangerousAddRef(ref mustRelease);
  989. // We absolutely must have the value of waitResult set,
  990. // or we may leak the mutex in async abort cases.
  991. RuntimeHelpers.PrepareConstrainedRegions();
  992. try {
  993. Debug.Assert(2 == waitHandleCount || 3 == waitHandleCount, "unexpected waithandle count");
  994. }
  995. finally {
  996. waitResult = SafeNativeMethods.WaitForMultipleObjectsEx(waitHandleCount, _waitHandles.DangerousGetHandle(), false, waitForMultipleObjectsTimeout, false);
  997. // VSTFDEVDIV 479551 - call GetHRForLastWin32Error immediately after after the native call
  998. if (waitResult == WAIT_FAILED) {
  999. waitForMultipleObjectsExHR = Marshal.GetHRForLastWin32Error();
  1000. }
  1001. }
  1002. // From the WaitAny docs: "If more than one object became signaled during
  1003. // the call, this is the array index of the signaled object with the
  1004. // smallest index value of all the signaled objects." This is important
  1005. // so that the free object signal will be returned before a creation
  1006. // signal.
  1007. switch (waitResult) {
  1008. case WAIT_TIMEOUT:
  1009. Bid.PoolerTrace("<prov.DbConnectionPool.GetConnection|RES|CPOOL> %d#, Wait timed out.\n", ObjectID);
  1010. Interlocked.Decrement(ref _waitCount);
  1011. connection = null;
  1012. return false;
  1013. case ERROR_HANDLE:
  1014. // Throw the error that PoolCreateRequest stashed.
  1015. Bid.PoolerTrace("<prov.DbConnectionPool.GetConnection|RES|CPOOL> %d#, Errors are set.\n", ObjectID);
  1016. Interlocked.Decrement(ref _waitCount);
  1017. throw TryCloneCachedException();
  1018. case CREATION_HANDLE:
  1019. Bid.PoolerTrace("<prov.DbConnectionPool.GetConnection|RES|CPOOL> %d#, Creating new connection.\n", ObjectID);
  1020. try {
  1021. obj = UserCreateRequest(owningObject, userOptions);
  1022. }
  1023. catch {
  1024. if (null == obj) {
  1025. Interlocked.Decrement(ref _waitCount);
  1026. }
  1027. throw;
  1028. }
  1029. finally {
  1030. // SQLBUDT #386664 - ensure that we release this waiter, regardless
  1031. // of any exceptions that may be thrown.
  1032. if (null != obj) {
  1033. Interlocked.Decrement(ref _waitCount);
  1034. }
  1035. }
  1036. if (null == obj) {
  1037. // If we were not able to create an object, check to see if
  1038. // we reached MaxPoolSize. If so, we will no longer wait on
  1039. // the CreationHandle, but instead wait for a free object or
  1040. // the timeout.
  1041. //
  1042. if (Count >= MaxPoolSize && 0 != MaxPoolSize) {
  1043. if (!ReclaimEmancipatedObjects()) {
  1044. // modify handle array not to wait on creation mutex anymore
  1045. Debug.Assert(2 == CREATION_HANDLE, "creation handle changed value");
  1046. waitHandleCount = 2;
  1047. }
  1048. }
  1049. }
  1050. break;
  1051. case SEMAPHORE_HANDLE:
  1052. //
  1053. // guaranteed available inventory
  1054. //
  1055. Interlocked.Decrement(ref _waitCount);
  1056. obj = GetFromGeneralPool();
  1057. if ((obj != null) && (!obj.IsConnectionAlive())) {
  1058. Bid.PoolerTrace("<prov.DbConnectionPool.GetConnection|RES|CPOOL> %d#, Connection %d#, found dead and removed.\n", ObjectID, obj.ObjectID);
  1059. DestroyObject(obj);
  1060. obj = null; // Setting to null in case creating a new object fails
  1061. if (onlyOneCheckConnection) {
  1062. if (_waitHandles.CreationSemaphore.WaitOne(unchecked((int)waitForMultipleObjectsTimeout))) {
  1063. RuntimeHelpers.PrepareConstrainedRegions();
  1064. try {
  1065. Bid.PoolerTrace("<prov.DbConnectionPool.GetConnection|RES|CPOOL> %d#, Creating new connection.\n", ObjectID);
  1066. obj = UserCreateRequest(owningObject, userOptions);
  1067. }
  1068. finally {
  1069. _waitHandles.CreationSemaphore.Release(1);
  1070. }
  1071. }
  1072. else {
  1073. // Timeout waiting for creation semaphore - return null
  1074. Bid.PoolerTrace("<prov.DbConnectionPool.GetConnection|RES|CPOOL> %d#, Wait timed out.\n", ObjectID);
  1075. connection = null;
  1076. return false;
  1077. }
  1078. }
  1079. }
  1080. break;
  1081. case WAIT_FAILED:
  1082. Debug.Assert(waitForMultipleObjectsExHR != 0, "WaitForMultipleObjectsEx failed but waitForMultipleObjectsExHR remained 0");
  1083. Bid.PoolerTrace("<prov.DbConnectionPool.GetConnection|RES|CPOOL> %d#, Wait failed.\n", ObjectID);
  1084. Interlocked.Decrement(ref _waitCount);
  1085. Marshal.ThrowExceptionForHR(waitForMultipleObjectsExHR);
  1086. goto default; // if ThrowExceptionForHR didn't throw for some reason
  1087. case (WAIT_ABANDONED+SEMAPHORE_HANDLE):
  1088. Bid.PoolerTrace("<prov.DbConnectionPool.GetConnection|RES|CPOOL> %d#, Semaphore handle abandonded.\n", ObjectID);
  1089. Interlocked.Decrement(ref _waitCount);
  1090. throw new AbandonedMutexException(SEMAPHORE_HANDLE,_waitHandles.PoolSemaphore);
  1091. case (WAIT_ABANDONED+ERROR_HANDLE):
  1092. Bid.PoolerTrace("<prov.DbConnectionPool.GetConnection|RES|CPOOL> %d#, Error handle abandonded.\n", ObjectID);
  1093. Interlocked.Decrement(ref _waitCount);
  1094. throw new AbandonedMutexException(ERROR_HANDLE,_waitHandles.ErrorEvent);
  1095. case (WAIT_ABANDONED+CREATION_HANDLE):
  1096. Bid.PoolerTrace("<prov.DbConnectionPool.GetConnection|RES|CPOOL> %d#, Creation handle abandoned.\n", ObjectID);
  1097. Interlocked.Decrement(ref _waitCount);
  1098. throw new AbandonedMutexException(CREATION_HANDLE,_waitHandles.CreationSemaphore);
  1099. default:
  1100. Bid.PoolerTrace("<prov.DbConnectionPool.GetConnection|RES|CPOOL> %d#, WaitForMultipleObjects=%d\n", ObjectID, waitResult);
  1101. Interlocked.Decrement(ref _waitCount);
  1102. throw ADP.InternalError(ADP.InternalErrorCode.UnexpectedWaitAnyResult);
  1103. }
  1104. }
  1105. finally {
  1106. if (CREATION_HANDLE == waitResult) {
  1107. int result = SafeNativeMethods.ReleaseSemaphore(_waitHandles.CreationHandle.DangerousGetHandle(), 1, IntPtr.Zero);
  1108. if (0 == result) { // failure case
  1109. releaseSemaphoreResult = Marshal.GetHRForLastWin32Error();
  1110. }
  1111. }
  1112. if (mustRelease) {
  1113. _waitHandles.DangerousRelease();
  1114. }
  1115. }
  1116. if (0 != releaseSemaphoreResult) {
  1117. Marshal.ThrowExceptionForHR(releaseSemaphoreResult); // will only throw if (hresult < 0)
  1118. }
  1119. } while (null == obj);
  1120. }
  1121. if (null != obj)
  1122. {
  1123. PrepareConnection(owningObject, obj, transaction);
  1124. }
  1125. connection = obj;
  1126. return true;
  1127. }
  1128. private void PrepareConnection(DbConnection owningObject, DbConnectionInternal obj, SysTx.Transaction transaction) {
  1129. lock (obj)
  1130. { // Protect against Clear and ReclaimEmancipatedObjects, which call IsEmancipated, which is affected by PrePush and PostPop
  1131. obj.PostPop(owningObject);
  1132. }
  1133. try
  1134. {
  1135. obj.ActivateConnection(transaction);
  1136. }
  1137. catch
  1138. {
  1139. // if Activate throws an exception
  1140. // put it back in the pool or have it properly disposed of
  1141. this.PutObject(obj, owningObject);
  1142. throw;
  1143. }
  1144. }
  1145. /// <summary>
  1146. /// Creates a new connection to replace an existing connection
  1147. /// </summary>
  1148. /// <param name="owningObject">Outer connection that currently owns <paramref name="oldConnection"/></param>
  1149. /// <param name="userOptions">Options used to create the new connection</param>
  1150. /// <param name="oldConnection">Inner connection that will be replaced</param>
  1151. /// <returns>A new inner connection that is attached to the <paramref name="owningObject"/></returns>
  1152. internal DbConnectionInternal ReplaceConnection(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection) {
  1153. #if !MOBILE
  1154. PerformanceCounters.SoftConnectsPerSecond.Increment();
  1155. #endif
  1156. Bid.PoolerTrace("<prov.DbConnectionPool.ReplaceConnection|RES|CPOOL> %d#, replacing connection.\n", ObjectID);
  1157. DbConnectionInternal newConnection = UserCreateRequest(owningObject, userOptions, oldConnection);
  1158. if (newConnection != null) {
  1159. PrepareConnection(owningObject, newConnection, oldConnection.EnlistedTransaction);
  1160. oldConnection.PrepareForReplaceConnection();
  1161. oldConnection.DeactivateConnection();
  1162. oldConnection.Dispose();
  1163. }
  1164. return newConnection;
  1165. }
  1166. private DbConnectionInternal GetFromGeneralPool() {
  1167. DbConnectionInternal obj = null;
  1168. if (!_stackNew.TryPop(out obj)) {
  1169. if (!_stackOld.TryPop(out obj)) {
  1170. obj = null;
  1171. }
  1172. else {
  1173. Debug.Assert(obj != null, "null connection is not expected");
  1174. }
  1175. }
  1176. else {
  1177. Debug.Assert(obj != null, "null connection is not expected");
  1178. }
  1179. // SQLBUDT #356870 -- When another thread is clearing this pool,
  1180. // it will remove all connections in this pool which causes the
  1181. // following assert to fire, which really mucks up stress against
  1182. // checked bits. The assert is benign, so we're commenting it out.
  1183. //Debug.Assert(obj != null, "GetFromGeneralPool called with nothing in the pool!");
  1184. if (null != obj) {
  1185. Bid.PoolerTrace("<prov.DbConnectionPool.GetFromGeneralPool|RES|CPOOL> %d#, Connection %d#, Popped from general pool.\n", ObjectID, obj.ObjectID);
  1186. #if !MOBILE
  1187. PerformanceCounters.NumberOfFreeConnections.Decrement();
  1188. #endif
  1189. }
  1190. return(obj);
  1191. }
  1192. private DbConnectionInternal GetFromTransactedPool(out SysTx.Transaction transaction) {
  1193. transaction = ADP.GetCurrentTransaction();
  1194. DbConnectionInternal obj = null;
  1195. if (null != transaction && null != _transactedConnectionPool) {
  1196. obj = _transactedConnectionPool.GetTransactedObject(transaction);
  1197. if (null != obj) {
  1198. Bid.PoolerTrace("<prov.DbConnectionPool.GetFromTransactedPool|RES|CPOOL> %d#, Connection %d#, Popped from transacted pool.\n", ObjectID, obj.ObjectID);
  1199. #if !MOBILE
  1200. PerformanceCounters.NumberOfFreeConnections.Decrement();
  1201. #endif
  1202. if (obj.IsTransactionRoot) {
  1203. try {
  1204. obj.IsConnectionAlive(true);
  1205. }
  1206. catch {
  1207. Bid.PoolerTrace("<prov.DbConnectionPool.GetFromTransactedPool|RES|CPOOL> %d#, Connection %d#, found dead and removed.\n", ObjectID, obj.ObjectID);
  1208. DestroyObject(obj);
  1209. throw;
  1210. }
  1211. }
  1212. else if (!obj.IsConnectionAlive()) {
  1213. Bid.PoolerTrace("<prov.DbConnectionPool.GetFromTransactedPool|RES|CPOOL> %d#, Connection %d#, found dead and removed.\n", ObjectID, obj.ObjectID);
  1214. DestroyObject(obj);
  1215. obj = null;
  1216. }
  1217. }
  1218. }
  1219. return obj;
  1220. }
  1221. [ResourceExposure(ResourceScope.None)] // SxS: this method does not expose resources
  1222. [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
  1223. private void PoolCreateRequest(object state) {
  1224. // called by pooler to ensure pool requests are currently being satisfied -
  1225. // creation mutex has not been obtained
  1226. IntPtr hscp;
  1227. Bid.PoolerScopeEnter(out hscp, "<prov.DbConnectionPool.PoolCreateRequest|RES|INFO|CPOOL> %d#\n", ObjectID);
  1228. try {
  1229. if (State.Running == _state) {
  1230. // in case WaitForPendingOpen ever failed with no subsequent OpenAsync calls,
  1231. // start it back up again
  1232. if (!_pendingOpens.IsEmpty && _pendingOpensWaiting == 0) {
  1233. Thread waitOpenThread = new Thread(WaitForPendingOpen);
  1234. waitOpenThread.IsBackground = true;
  1235. waitOpenThread.Start();
  1236. }
  1237. // Before creating any new objects, reclaim any released objects that were
  1238. // not closed.
  1239. ReclaimEmancipatedObjects();
  1240. if (!ErrorOccurred) {
  1241. if (NeedToReplenish) {
  1242. // Check to see if pool was created using integrated security and if so, make
  1243. // sure the identity of current user matches that of user that created pool.
  1244. // If it doesn't match, do not create any objects on the ThreadPool thread,
  1245. // since either Open will fail or we will open a object for this pool that does
  1246. // not belong in this pool. The side effect of this is that if using integrated
  1247. // security min pool size cannot be guaranteed.
  1248. if (UsingIntegrateSecurity && !_identity.Equals(DbConnectionPoolIdentity.GetCurrent())) {
  1249. return;
  1250. }
  1251. bool mustRelease = false;
  1252. int waitResult = BOGUS_HANDLE;
  1253. uint timeout = (uint)CreationTimeout;
  1254. RuntimeHelpers.PrepareConstrainedRegions();
  1255. try {
  1256. _waitHandles.DangerousAddRef(ref mustRelease);
  1257. // Obtain creation mutex so we're the only one creating objects
  1258. // and we must have the wait result
  1259. RuntimeHelpers.PrepareConstrainedRegions();
  1260. try { } finally {
  1261. waitResult = SafeNativeMethods.WaitForSingleObjectEx(_waitHandles.CreationHandle.DangerousGetHandle(), timeout, false);
  1262. }
  1263. if (WAIT_OBJECT_0 == waitResult) {
  1264. DbConnectionInternal newObj;
  1265. // Check ErrorOccurred again after obtaining mutex
  1266. if (!ErrorOccurred) {
  1267. while (NeedToReplenish) {
  1268. // Don't specify any user options because there is no outer connection associated with the new connection
  1269. newObj = CreateObject(owningObject: null, userOptions: null, oldConnection: null);
  1270. // We do not need to check error flag here, since we know if
  1271. // CreateObject returned null, we are in error case.
  1272. if (null != newObj) {
  1273. PutNewObject(newObj);
  1274. }
  1275. else {
  1276. break;
  1277. }
  1278. }
  1279. }
  1280. }
  1281. else if (WAIT_TIMEOUT == waitResult) {
  1282. // do not wait forever and potential block this worker thread
  1283. // instead wait for a period of time and just requeue to try again
  1284. QueuePoolCreateRequest();
  1285. }
  1286. else {
  1287. // trace waitResult and ignore the failure
  1288. Bid.PoolerTrace("<prov.DbConnectionPool.PoolCreateRequest|RES|CPOOL> %d#, PoolCreateRequest called WaitForSingleObject failed %d", ObjectID, waitResult);
  1289. }
  1290. }
  1291. catch (Exception e) {
  1292. //
  1293. if (!ADP.IsCatchableExceptionType(e)) {
  1294. throw;
  1295. }
  1296. // Now that CreateObject can throw, we need to catch the exception and discard it.
  1297. // There is no further action we can take beyond tracing. The error will be
  1298. // thrown to the user the next time they request a connection.
  1299. Bid.PoolerTrace("<prov.DbConnectionPool.PoolCreateRequest|RES|CPOOL> %d#, PoolCreateRequest called CreateConnection which threw an exception: %ls", ObjectID, e);
  1300. }
  1301. finally {
  1302. if (WAIT_OBJECT_0 == waitResult) {
  1303. // reuse waitResult and ignore its value
  1304. waitResult = SafeNativeMethods.ReleaseSemaphore(_waitHandles.CreationHandle.DangerousGetHandle(), 1, IntPtr.Zero);
  1305. }
  1306. if (mustRelease) {
  1307. _waitHandles.DangerousRelease();
  1308. }
  1309. }
  1310. }
  1311. }
  1312. }
  1313. }
  1314. finally {
  1315. Bid.ScopeLeave(ref hscp);
  1316. }
  1317. }
  1318. internal void PutNewObject(DbConnectionInternal obj) {
  1319. Debug.Assert(null != obj, "why are we adding a null object to the pool?");
  1320. // VSTFDEVDIV 742887 - When another thread is clearing this pool, it
  1321. // will set _cannotBePooled for all connections in this pool without prejudice which
  1322. // causes the following assert to fire, which really mucks up stress
  1323. // against checked bits.
  1324. // Debug.Assert(obj.CanBePooled, "non-poolable object in pool");
  1325. Bid.PoolerTrace("<prov.DbConnectionPool.PutNewObject|RES|CPOOL> %d#, Connection %d#, Pushing to general pool.\n", ObjectID, obj.ObjectID);
  1326. _stackNew.Push(obj);
  1327. _waitHandles.PoolSemaphore.Release(1);
  1328. #if !MOBILE
  1329. PerformanceCounters.NumberOfFreeConnections.Increment();
  1330. #endif
  1331. }
  1332. internal void PutObject(DbConnectionInternal obj, object owningObject) {
  1333. Debug.Assert(null != obj, "null obj?");
  1334. #if !MOBILE
  1335. PerformanceCounters.SoftDisconnectsPerSecond.Increment();
  1336. #endif
  1337. // Once a connection is closing (which is the state that we're in at
  1338. // this point in time) you cannot delegate a transaction to or enlist
  1339. // a transaction in it, so we can correctly presume that if there was
  1340. // not a delegated or enlisted transaction to start with, that there
  1341. // will not be a delegated or enlisted transaction once we leave the
  1342. // lock.
  1343. lock (obj) {
  1344. // Calling PrePush prevents the object from being reclaimed
  1345. // once we leave the lock, because it sets _pooledCount such
  1346. // that it won't appear to be out of the pool. What that
  1347. // means, is that we're now responsible for this connection:
  1348. // it won't get reclaimed if we drop the ball somewhere.
  1349. obj.PrePush(owningObject);
  1350. //
  1351. }
  1352. DeactivateObject(obj);
  1353. }
  1354. internal void PutObjectFromTransactedPool(DbConnectionInternal obj) {
  1355. Debug.Assert(null != obj, "null pooledObject?");
  1356. Debug.Assert(obj.EnlistedTransaction == null, "pooledObject is still enlisted?");
  1357. // called by the transacted connection pool , once it's removed the
  1358. // connection from it's list. We put the connection back in general
  1359. // circulation.
  1360. // NOTE: there is no locking required here because if we're in this
  1361. // method, we can safely presume that the caller is the only person
  1362. // that is using the connection, and that all pre-push logic has been
  1363. // done and all transactions are ended.
  1364. Bid.PoolerTrace("<prov.DbConnectionPool.PutObjectFromTransactedPool|RES|CPOOL> %d#, Connection %d#, Transaction has ended.\n", ObjectID, obj.ObjectID);
  1365. if (_state == State.Running && obj.CanBePooled) {
  1366. PutNewObject(obj);
  1367. }
  1368. else {
  1369. DestroyObject(obj);
  1370. QueuePoolCreateRequest();
  1371. }
  1372. }
  1373. private void QueuePoolCreateRequest() {
  1374. if (State.Running == _state) {
  1375. // Make sure we're at quota by posting a callback to the threadpool.
  1376. ThreadPool.QueueUserWorkItem(_poolCreateRequest);
  1377. }
  1378. }
  1379. private bool ReclaimEmancipatedObjects() {
  1380. bool emancipatedObjectFound = false;
  1381. Bid.PoolerTrace("<prov.DbConnectionPool.ReclaimEmancipatedObjects|RES|CPOOL> %d#\n", ObjectID);
  1382. List<DbConnectionInternal> reclaimedObjects = new List<DbConnectionInternal>();
  1383. int count;
  1384. lock(_objectList) {
  1385. count = _objectList.Count;
  1386. for (int i = 0; i < count; ++i) {
  1387. DbConnectionInternal obj = _objectList[i];
  1388. if (null != obj) {
  1389. bool locked = false;
  1390. try {
  1391. Monitor.TryEnter(obj, ref locked);
  1392. if (locked) { // avoid race condition with PrePush/PostPop and IsEmancipated
  1393. if (obj.IsEmancipated) {
  1394. // Inside the lock, we want to do as little
  1395. // as possible, so we simply mark the object
  1396. // as being in the pool, but hand it off to
  1397. // an out of pool list to be deactivated,
  1398. // etc.
  1399. obj.PrePush(null);
  1400. reclaimedObjects.Add(obj);
  1401. }
  1402. }
  1403. }
  1404. finally {
  1405. if (locked)
  1406. Monitor.Exit(obj);
  1407. }
  1408. }
  1409. }
  1410. }
  1411. // NOTE: we don't want to call DeactivateObject while we're locked,
  1412. // because it can make roundtrips to the server and this will block
  1413. // object creation in the pooler. Instead, we queue things we need
  1414. // to do up, and process them outside the lock.
  1415. count = reclaimedObjects.Count;
  1416. for (int i = 0; i < count; ++i) {
  1417. DbConnectionInternal obj = reclaimedObjects[i];
  1418. Bid.PoolerTrace("<prov.DbConnectionPool.ReclaimEmancipatedObjects|RES|CPOOL> %d#, Connection %d#, Reclaiming.\n", ObjectID, obj.ObjectID);
  1419. #if !MOBILE
  1420. PerformanceCounters.NumberOfReclaimedConnections.Increment();
  1421. #endif
  1422. emancipatedObjectFound = true;
  1423. obj.DetachCurrentTransactionIfEnded();
  1424. DeactivateObject(obj);
  1425. }
  1426. return emancipatedObjectFound;
  1427. }
  1428. internal void Startup() {
  1429. Bid.PoolerTrace("<prov.DbConnectionPool.Startup|RES|INFO|CPOOL> %d#, CleanupWait=%d\n", ObjectID, _cleanupWait);
  1430. _cleanupTimer = CreateCleanupTimer();
  1431. if (NeedToReplenish) {
  1432. QueuePoolCreateRequest();
  1433. }
  1434. }
  1435. internal void Shutdown() {
  1436. Bid.PoolerTrace("<prov.DbConnectionPool.Shutdown|RES|INFO|CPOOL> %d#\n", ObjectID);
  1437. _state = State.ShuttingDown;
  1438. // deactivate timer callbacks
  1439. Timer t = _cleanupTimer;
  1440. _cleanupTimer = null;
  1441. if (null != t) {
  1442. t.Dispose();
  1443. }
  1444. }
  1445. // TransactionEnded merely provides the plumbing for DbConnectionInternal to access the transacted pool
  1446. // that is implemented inside DbConnectionPool. This method's counterpart (PutTransactedObject) should
  1447. // only be called from DbConnectionPool.DeactivateObject and thus the plumbing to provide access to
  1448. // other objects is unnecessary (hence the asymmetry of Ended but no Begin)
  1449. internal void TransactionEnded(SysTx.Transaction transaction, DbConnectionInternal transactedObject) {
  1450. Debug.Assert(null != transaction, "null transaction?");
  1451. Debug.Assert(null != transactedObject, "null transactedObject?");
  1452. // Note: connection may still be associated with transaction due to Explicit Unbinding requirement.
  1453. Bid.PoolerTrace("<prov.DbConnectionPool.TransactionEnded|RES|CPOOL> %d#, Transaction %d#, Connection %d#, Transaction Completed\n", ObjectID, transaction.GetHashCode(), transactedObject.ObjectID);
  1454. // called by the internal connection when it get's told that the
  1455. // transaction is completed. We tell the transacted pool to remove
  1456. // the connection from it's list, then we put the connection back in
  1457. // general circulation.
  1458. TransactedConnectionPool transactedConnectionPool = _transactedConnectionPool;
  1459. if (null != transactedConnectionPool) {
  1460. transactedConnectionPool.TransactionEnded(transaction, transactedObject);
  1461. }
  1462. }
  1463. private DbConnectionInternal UserCreateRequest(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection = null) {
  1464. // called by user when they were not able to obtain a free object but
  1465. // instead obtained creation mutex
  1466. DbConnectionInternal obj = null;
  1467. if (ErrorOccurred) {
  1468. throw TryCloneCachedException();
  1469. }
  1470. else {
  1471. if ((oldConnection != null) || (Count < MaxPoolSize) || (0 == MaxPoolSize)) {
  1472. // If we have an odd number of total objects, reclaim any dead objects.
  1473. // If we did not find any objects to reclaim, create a new one.
  1474. //
  1475. if ((oldConnection != null) || (Count & 0x1) == 0x1 || !ReclaimEmancipatedObjects())
  1476. obj = CreateObject(owningObject, userOptions, oldConnection);
  1477. }
  1478. return obj;
  1479. }
  1480. }
  1481. }
  1482. }