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