1 //------------------------------------------------------------------------------
2 // <copyright file="DbConnectionInternal.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 // <owner current="true" primary="true">[....]</owner>
6 // <owner current="true" primary="false">[....]</owner>
7 //------------------------------------------------------------------------------
9 namespace System.Data.ProviderBase {
12 using System.ComponentModel;
14 using System.Data.Common;
15 using System.Diagnostics;
16 using System.Globalization;
17 using System.Runtime.ConstrainedExecution;
18 using System.Runtime.InteropServices;
19 using System.Runtime.InteropServices.ComTypes;
20 using System.Security;
21 using System.Security.Permissions;
22 using System.Threading;
23 using System.Threading.Tasks;
24 using SysTx = System.Transactions;
26 internal abstract class DbConnectionInternal { // V1.1.3300
29 private static int _objectTypeCount;
30 internal readonly int _objectID = Interlocked.Increment(ref _objectTypeCount);
32 internal static readonly StateChangeEventArgs StateChangeClosed = new StateChangeEventArgs(ConnectionState.Open, ConnectionState.Closed);
33 internal static readonly StateChangeEventArgs StateChangeOpen = new StateChangeEventArgs(ConnectionState.Closed, ConnectionState.Open);
35 private readonly bool _allowSetConnectionString;
36 private readonly bool _hidePassword;
37 private readonly ConnectionState _state;
39 private readonly WeakReference _owningObject = new WeakReference(null, false); // [usage must be thread safe] the owning object, when not in the pool. (both Pooled and Non-Pooled connections)
41 private DbConnectionPool _connectionPool; // the pooler that the connection came from (Pooled connections only)
42 private DbConnectionPoolCounters _performanceCounters; // the performance counters we're supposed to update
43 private DbReferenceCollection _referenceCollection; // collection of objects that we need to notify in some way when we're being deactivated
44 private int _pooledCount; // [usage must be thread safe] the number of times this object has been pushed into the pool less the number of times it's been popped (0 != inPool)
46 private bool _connectionIsDoomed; // true when the connection should no longer be used.
47 private bool _cannotBePooled; // true when the connection should no longer be pooled.
48 private bool _isInStasis;
50 private DateTime _createTime; // when the connection was created.
52 private SysTx.Transaction _enlistedTransaction; // [usage must be thread-safe] the transaction that we're enlisted in, either manually or automatically
54 // _enlistedTransaction is a clone, so that transaction information can be queried even if the original transaction object is disposed.
55 // However, there are times when we need to know if the original transaction object was disposed, so we keep a reference to it here.
56 // This field should only be assigned a value at the same time _enlistedTransaction is updated.
57 // Also, this reference should not be disposed, since we aren't taking ownership of it.
58 private SysTx.Transaction _enlistedTransactionOriginal;
61 private int _activateCount; // debug only counter to verify activate/deactivates are in [....].
64 protected DbConnectionInternal() : this(ConnectionState.Open, true, false) { // V1.1.3300
67 // Constructor for internal connections
68 internal DbConnectionInternal(ConnectionState state, bool hidePassword, bool allowSetConnectionString) {
69 _allowSetConnectionString = allowSetConnectionString;
70 _hidePassword = hidePassword;
74 internal bool AllowSetConnectionString {
76 return _allowSetConnectionString;
80 internal bool CanBePooled {
82 bool flag = (!_connectionIsDoomed && !_cannotBePooled && !_owningObject.IsAlive);
87 protected internal SysTx.Transaction EnlistedTransaction {
89 return _enlistedTransaction;
92 SysTx.Transaction currentEnlistedTransaction = _enlistedTransaction;
93 if (((null == currentEnlistedTransaction) && (null != value))
94 || ((null != currentEnlistedTransaction) && !currentEnlistedTransaction.Equals(value))) { // WebData 20000024
96 // Pay attention to the order here:
97 // 1) defect from any notifications
98 // 2) replace the transaction
99 // 3) re-enlist in notifications for the new transaction
101 // SQLBUDT #230558 we need to use a clone of the transaction
102 // when we store it, or we'll end up keeping it past the
103 // duration of the using block of the TransactionScope
104 SysTx.Transaction valueClone = null;
105 SysTx.Transaction previousTransactionClone = null;
108 valueClone = value.Clone();
111 // NOTE: rather than take locks around several potential round-
112 // trips to the server, and/or virtual function calls, we simply
113 // presume that you aren't doing something illegal from multiple
114 // threads, and check once we get around to finalizing things
118 // NOTE: There is still a race condition here, when we are
119 // called from EnlistTransaction (which cannot re-enlist)
120 // instead of EnlistDistributedTransaction (which can),
121 // however this should have been handled by the outer
122 // connection which checks to ensure that it's OK. The
123 // only case where we have the race condition is multiple
124 // concurrent enlist requests to the same connection, which
125 // is a bit out of line with something we should have to
128 // enlisted transaction can be nullified in Dispose call without lock
129 previousTransactionClone = Interlocked.Exchange(ref _enlistedTransaction, valueClone);
130 _enlistedTransactionOriginal = value;
132 valueClone = null; // we've stored it, don't dispose it.
136 // we really need to dispose our clones; they may have
137 // native resources and GC may not happen soon enough.
138 // VSDevDiv 479564: don't dispose if still holding reference in _enlistedTransaction
139 if (null != previousTransactionClone &&
140 !Object.ReferenceEquals(previousTransactionClone, _enlistedTransaction)) {
141 previousTransactionClone.Dispose();
143 if (null != valueClone && !Object.ReferenceEquals(valueClone, _enlistedTransaction)) {
144 valueClone.Dispose();
148 // I don't believe that we need to lock to protect the actual
149 // enlistment in the transaction; it would only protect us
150 // against multiple concurrent calls to enlist, which really
151 // isn't supported anyway.
154 if (Bid.IsOn(DbConnectionPool.PoolerTracePoints)) {
155 int x = value.GetHashCode();
156 Bid.PoolerTrace("<prov.DbConnectionInternal.set_EnlistedTransaction|RES|CPOOL> %d#, Transaction %d#, Enlisting.\n", ObjectID, x);
158 TransactionOutcomeEnlist(value);
165 /// Get boolean value that indicates whether the enlisted transaction has been disposed.
168 /// True if there is an enlisted transaction, and it has been diposed.
169 /// False if there is an enlisted transaction that has not been disposed, or if the transaction reference is null.
172 /// This method must be called while holding a lock on the DbConnectionInternal instance.
174 protected bool EnlistedTransactionDisposed
178 // Until the Transaction.Disposed property is public it is necessary to access a member
179 // that throws if the object is disposed to determine if in fact the transaction is disposed.
184 SysTx.Transaction currentEnlistedTransactionOriginal = _enlistedTransactionOriginal;
185 if (currentEnlistedTransactionOriginal != null)
187 disposed = currentEnlistedTransactionOriginal.TransactionInformation == null;
191 // Don't expect to get here in the general case,
192 // Since this getter is called by CheckEnlistedTransactionBinding
193 // after checking for a non-null enlisted transaction (and it does so under lock).
199 catch (ObjectDisposedException)
206 // Is this connection in stasis, waiting for transaction to end before returning to pool?
207 internal bool IsTxRootWaitingForTxEnd {
214 /// Get boolean that specifies whether an enlisted transaction can be unbound from
215 /// the connection when that transaction completes.
218 /// True if the enlisted transaction can be unbound on transaction completion; otherwise false.
220 virtual protected bool UnbindOnTransactionCompletion
228 // Is this a connection that must be put in stasis (or is already in stasis) pending the end of it's transaction?
229 virtual protected internal bool IsNonPoolableTransactionRoot {
231 return false; // if you want to have delegated transactions that are non-poolable, you better override this...
235 virtual internal bool IsTransactionRoot {
237 return false; // if you want to have delegated transactions, you better override this...
241 protected internal bool IsConnectionDoomed {
243 return _connectionIsDoomed;
247 internal bool IsEmancipated {
249 // NOTE: There are race conditions between PrePush, PostPop and this
250 // property getter -- only use this while this object is locked;
251 // (DbConnectionPool.Clear and ReclaimEmancipatedObjects
254 // Remember how this works (I keep getting confused...)
256 // _pooledCount is incremented when the connection is pushed into the pool
257 // _pooledCount is decremented when the connection is popped from the pool
258 // _pooledCount is set to -1 when the connection is not pooled (just in case...)
262 // _pooledCount > 1 connection is in the pool multiple times (this is a serious bug...)
263 // _pooledCount == 1 connection is in the pool
264 // _pooledCount == 0 connection is out of the pool
265 // _pooledCount == -1 connection is not a pooled connection; we shouldn't be here for non-pooled connections.
266 // _pooledCount < -1 connection out of the pool multiple times (not sure how this could happen...)
268 // Now, our job is to return TRUE when the connection is out
269 // of the pool and it's owning object is no longer around to
272 bool value = !IsTxRootWaitingForTxEnd && (_pooledCount < 1) && !_owningObject.IsAlive;
277 internal bool IsInPool {
279 Debug.Assert(_pooledCount <= 1 && _pooledCount >= -1, "Pooled count for object is invalid");
280 return (_pooledCount == 1);
284 internal int ObjectID {
290 protected internal object Owner {
291 // We use a weak reference to the owning object so we can identify when
292 // it has been garbage collected without thowing exceptions.
294 return _owningObject.Target;
298 internal DbConnectionPool Pool {
300 return _connectionPool;
304 protected DbConnectionPoolCounters PerformanceCounters {
306 return _performanceCounters;
310 virtual protected bool ReadyToPrepareTransaction {
316 protected internal DbReferenceCollection ReferenceCollection {
318 return _referenceCollection;
322 abstract public string ServerVersion {
326 // this should be abstract but untill it is added to all the providers virtual will have to do [....]
327 virtual public string ServerVersionNormalized {
329 throw ADP.NotSupported();
333 public bool ShouldHidePassword {
335 return _hidePassword;
339 public ConnectionState State {
345 abstract protected void Activate(SysTx.Transaction transaction);
347 internal void ActivateConnection(SysTx.Transaction transaction) {
348 // Internal method called from the connection pooler so we don't expose
349 // the Activate method publicly.
351 Bid.PoolerTrace("<prov.DbConnectionInternal.ActivateConnection|RES|INFO|CPOOL> %d#, Activating\n", ObjectID);
353 int activateCount = Interlocked.Increment(ref _activateCount);
354 Debug.Assert(1 == activateCount, "activated multiple times?");
357 Activate(transaction);
360 PerformanceCounters.NumberOfActiveConnections.Increment();
364 internal void AddWeakReference(object value, int tag) {
365 if (null == _referenceCollection) {
366 _referenceCollection = CreateReferenceCollection();
367 if (null == _referenceCollection) {
368 throw ADP.InternalError(ADP.InternalErrorCode.CreateReferenceCollectionReturnedNull);
371 _referenceCollection.Add(value, tag);
374 abstract public DbTransaction BeginTransaction(IsolationLevel il);
376 virtual public void ChangeDatabase(string value) {
377 throw ADP.MethodNotImplemented("ChangeDatabase");
380 internal virtual void CloseConnection(DbConnection owningObject, DbConnectionFactory connectionFactory) {
381 // The implementation here is the implementation required for the
382 // "open" internal connections, since our own private "closed"
383 // singleton internal connection objects override this method to
384 // prevent anything funny from happening (like disposing themselves
385 // or putting them into a connection pool)
387 // Derived class should override DbConnectionInternal.Deactivate and DbConnectionInternal.Dispose
388 // for cleaning up after DbConnection.Close
389 // protected override void Deactivate() { // override DbConnectionInternal.Close
390 // // do derived class connection deactivation for both pooled & non-pooled connections
392 // public override void Dispose() { // override DbConnectionInternal.Close
393 // // do derived class cleanup
397 // overriding DbConnection.Close is also possible, but must provider for their own synchronization
398 // public override void Close() { // override DbConnection.Close
400 // // do derived class outer connection for both pooled & non-pooled connections
401 // // user must do their own synchronization here
404 // if the DbConnectionInternal derived class needs to close the connection it should
405 // delegate to the DbConnection if one exists or directly call dispose
406 // DbConnection owningObject = (DbConnection)Owner;
407 // if (null != owningObject) {
408 // owningObject.Close(); // force the closed state on the outer object.
414 ////////////////////////////////////////////////////////////////
415 // DON'T MESS WITH THIS CODE UNLESS YOU KNOW WHAT YOU'RE DOING!
416 ////////////////////////////////////////////////////////////////
417 Debug.Assert(null != owningObject, "null owningObject");
418 Debug.Assert(null != connectionFactory, "null connectionFactory");
420 Bid.PoolerTrace("<prov.DbConnectionInternal.CloseConnection|RES|CPOOL> %d# Closing.\n", ObjectID);
422 // if an exception occurs after the state change but before the try block
423 // the connection will be stuck in OpenBusy state. The commented out try-catch
424 // block doesn't really help because a ThreadAbort during the finally block
425 // would just refert the connection to a bad state.
426 // Open->Closed: guarantee internal connection is returned to correct pool
427 if (connectionFactory.SetInnerConnectionFrom(owningObject, DbConnectionOpenBusy.SingletonInstance, this)) {
429 // Lock to prevent race condition with cancellation
432 object lockToken = ObtainAdditionalLocksForClose();
434 PrepareForCloseConnection();
436 DbConnectionPool connectionPool = Pool;
438 // Detach from enlisted transactions that are no longer active on close
439 DetachCurrentTransactionIfEnded();
441 // The singleton closed classes won't have owners and
442 // connection pools, and we won't want to put them back
444 if (null != connectionPool) {
445 connectionPool.PutObject(this, owningObject); // PutObject calls Deactivate for us...
446 // NOTE: Before we leave the PutObject call, another
447 // thread may have already popped the connection from
448 // the pool, so don't expect to be able to verify it.
451 Deactivate(); // ensure we de-activate non-pooled connections, or the data readers and transactions may not get cleaned up...
454 PerformanceCounters.HardDisconnectsPerSecond.Increment();
457 // To prevent an endless recursion, we need to clear
458 // the owning object before we call dispose so that
459 // we can't get here a second time... Ordinarily, I
460 // would call setting the owner to null a hack, but
461 // this is safe since we're about to dispose the
462 // object and it won't have an owner after that for
464 _owningObject.Target = null;
466 if (IsTransactionRoot) {
470 #if MONO_PARTIAL_DATA_IMPORT
474 PerformanceCounters.NumberOfNonPooledConnections.Decrement();
476 if (this.GetType() != typeof(System.Data.SqlClient.SqlInternalConnectionSmi))
485 ReleaseAdditionalLocksForClose(lockToken);
486 // if a ThreadAbort puts us here then its possible the outer connection will not reference
487 // this and this will be orphaned, not reclaimed by object pool until outer connection goes out of scope.
488 connectionFactory.SetInnerConnectionEvent(owningObject, DbConnectionClosedPreviouslyOpened.SingletonInstance);
494 virtual internal void PrepareForReplaceConnection() {
495 // By default, there is no preperation required
498 virtual protected void PrepareForCloseConnection() {
499 // By default, there is no preperation required
502 virtual protected object ObtainAdditionalLocksForClose() {
503 return null; // no additional locks in default implementation
506 virtual protected void ReleaseAdditionalLocksForClose(object lockToken) {
507 // no additional locks in default implementation
510 virtual protected DbReferenceCollection CreateReferenceCollection() {
511 throw ADP.InternalError(ADP.InternalErrorCode.AttemptingToConstructReferenceCollectionOnStaticObject);
514 abstract protected void Deactivate();
516 internal void DeactivateConnection() {
517 // Internal method called from the connection pooler so we don't expose
518 // the Deactivate method publicly.
520 Bid.PoolerTrace("<prov.DbConnectionInternal.DeactivateConnection|RES|INFO|CPOOL> %d#, Deactivating\n", ObjectID);
522 int activateCount = Interlocked.Decrement(ref _activateCount);
523 Debug.Assert(0 == activateCount, "activated multiple times?");
527 if (PerformanceCounters != null) { // Pool.Clear will DestroyObject that will clean performanceCounters before going here
528 PerformanceCounters.NumberOfActiveConnections.Decrement();
532 if (!_connectionIsDoomed && Pool.UseLoadBalancing) {
533 // If we're not already doomed, check the connection's lifetime and
534 // doom it if it's lifetime has elapsed.
536 DateTime now = DateTime.UtcNow; // WebData 111116
537 if ((now.Ticks - _createTime.Ticks) > Pool.LoadBalanceTimeout.Ticks) {
538 DoNotPoolThisConnection();
544 virtual internal void DelegatedTransactionEnded() {
545 // Called by System.Transactions when the delegated transaction has
546 // completed. We need to make closed connections that are in stasis
547 // available again, or disposed closed/leaked non-pooled connections.
549 // IMPORTANT NOTE: You must have taken a lock on the object before
550 // you call this method to prevent race conditions with Clear and
551 // ReclaimEmancipatedObjects.
553 Bid.Trace("<prov.DbConnectionInternal.DelegatedTransactionEnded|RES|CPOOL> %d#, Delegated Transaction Completed.\n", ObjectID);
555 if (1 == _pooledCount) {
556 // When _pooledCount is 1, it indicates a closed, pooled,
557 // connection so it is ready to put back into the pool for
560 TerminateStasis(true);
562 Deactivate(); // call it one more time just in case
564 DbConnectionPool pool = Pool;
567 throw ADP.InternalError(ADP.InternalErrorCode.PooledObjectWithoutPool); // pooled connection does not have a pool
569 pool.PutObjectFromTransactedPool(this);
571 else if (-1 == _pooledCount && !_owningObject.IsAlive) {
572 // When _pooledCount is -1 and the owning object no longer exists,
573 // it indicates a closed (or leaked), non-pooled connection so
574 // it is safe to dispose.
576 TerminateStasis(false);
578 Deactivate(); // call it one more time just in case
580 // it's a non-pooled connection, we need to dispose of it
581 // once and for all, or the server will have fits about us
582 // leaving connections open until the client-side GC kicks
585 PerformanceCounters.NumberOfNonPooledConnections.Decrement();
589 // When _pooledCount is 0, the connection is a pooled connection
590 // that is either open (if the owning object is alive) or leaked (if
591 // the owning object is not alive) In either case, we can't muck
592 // with the connection here.
595 public virtual void Dispose()
597 _connectionPool = null;
598 _performanceCounters = null;
599 _connectionIsDoomed = true;
600 _enlistedTransactionOriginal = null; // should not be disposed
602 // Dispose of the _enlistedTransaction since it is a clone
603 // of the original reference.
604 // VSDD 780271 - _enlistedTransaction can be changed by another thread (TX end event)
605 SysTx.Transaction enlistedTransaction = Interlocked.Exchange(ref _enlistedTransaction, null);
606 if (enlistedTransaction != null)
608 enlistedTransaction.Dispose();
612 protected internal void DoNotPoolThisConnection() {
613 _cannotBePooled = true;
614 Bid.PoolerTrace("<prov.DbConnectionInternal.DoNotPoolThisConnection|RES|INFO|CPOOL> %d#, Marking pooled object as non-poolable so it will be disposed\n", ObjectID);
617 /// <devdoc>Ensure that this connection cannot be put back into the pool.</devdoc>
618 [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
619 protected internal void DoomThisConnection() {
620 _connectionIsDoomed = true;
621 Bid.PoolerTrace("<prov.DbConnectionInternal.DoomThisConnection|RES|INFO|CPOOL> %d#, Dooming\n", ObjectID);
624 abstract public void EnlistTransaction(SysTx.Transaction transaction);
626 virtual protected internal DataTable GetSchema(DbConnectionFactory factory, DbConnectionPoolGroup poolGroup, DbConnection outerConnection, string collectionName, string[] restrictions){
627 Debug.Assert(outerConnection != null,"outerConnection may not be null.");
629 DbMetaDataFactory metaDataFactory = factory.GetMetaDataFactory(poolGroup, this);
630 Debug.Assert(metaDataFactory != null,"metaDataFactory may not be null.");
632 return metaDataFactory.GetSchema(outerConnection, collectionName,restrictions);
635 internal void MakeNonPooledObject(object owningObject, DbConnectionPoolCounters performanceCounters) {
636 // Used by DbConnectionFactory to indicate that this object IS NOT part of
637 // a connection pool.
639 _connectionPool = null;
640 _performanceCounters = performanceCounters;
641 _owningObject.Target = owningObject;
645 internal void MakePooledConnection(DbConnectionPool connectionPool) {
646 // Used by DbConnectionFactory to indicate that this object IS part of
647 // a connection pool.
650 _createTime = DateTime.UtcNow; // WebData 111116
652 _connectionPool = connectionPool;
653 _performanceCounters = connectionPool.PerformanceCounters;
656 internal void NotifyWeakReference(int message) {
657 DbReferenceCollection referenceCollection = ReferenceCollection;
658 if (null != referenceCollection) {
659 referenceCollection.Notify(message);
663 internal virtual void OpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory) {
664 if (!TryOpenConnection(outerConnection, connectionFactory, null, null)) {
665 throw ADP.InternalError(ADP.InternalErrorCode.SynchronousConnectReturnedPending);
669 /// <devdoc>The default implementation is for the open connection objects, and
670 /// it simply throws. Our private closed-state connection objects
671 /// override this and do the correct thing.</devdoc>
672 // User code should either override DbConnectionInternal.Activate when it comes out of the pool
673 // or override DbConnectionFactory.CreateConnection when the connection is created for non-pooled connections
674 internal virtual bool TryOpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource<DbConnectionInternal> retry, DbConnectionOptions userOptions) {
675 throw ADP.ConnectionAlreadyOpen(State);
678 internal virtual bool TryReplaceConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource<DbConnectionInternal> retry, DbConnectionOptions userOptions) {
679 throw ADP.MethodNotImplemented("TryReplaceConnection");
682 protected bool TryOpenConnectionInternal(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource<DbConnectionInternal> retry, DbConnectionOptions userOptions) {
683 // ?->Connecting: prevent set_ConnectionString during Open
684 if (connectionFactory.SetInnerConnectionFrom(outerConnection, DbConnectionClosedConnecting.SingletonInstance, this)) {
685 DbConnectionInternal openConnection = null;
687 connectionFactory.PermissionDemand(outerConnection);
688 if (!connectionFactory.TryGetConnection(outerConnection, retry, userOptions, this, out openConnection)) {
693 // This should occure for all exceptions, even ADP.UnCatchableExceptions.
694 connectionFactory.SetInnerConnectionTo(outerConnection, this);
697 if (null == openConnection) {
698 connectionFactory.SetInnerConnectionTo(outerConnection, this);
699 throw ADP.InternalConnectionError(ADP.ConnectionError.GetConnectionReturnsNull);
701 connectionFactory.SetInnerConnectionEvent(outerConnection, openConnection);
707 internal void PrePush(object expectedOwner) {
708 // Called by DbConnectionPool when we're about to be put into it's pool, we
709 // take this opportunity to ensure ownership and pool counts are legit.
711 // IMPORTANT NOTE: You must have taken a lock on the object before
712 // you call this method to prevent race conditions with Clear and
713 // ReclaimEmancipatedObjects.
715 //3 // The following tests are retail assertions of things we can't allow to happen.
716 if (null == expectedOwner) {
717 if (null != _owningObject.Target) {
718 throw ADP.InternalError(ADP.InternalErrorCode.UnpooledObjectHasOwner); // new unpooled object has an owner
721 else if (_owningObject.Target != expectedOwner) {
722 throw ADP.InternalError(ADP.InternalErrorCode.UnpooledObjectHasWrongOwner); // unpooled object has incorrect owner
724 if (0 != _pooledCount) {
725 throw ADP.InternalError(ADP.InternalErrorCode.PushingObjectSecondTime); // pushing object onto stack a second time
727 if (Bid.IsOn(DbConnectionPool.PoolerTracePoints)) {
728 //DbConnection x = (expectedOwner as DbConnection);
729 Bid.PoolerTrace("<prov.DbConnectionInternal.PrePush|RES|CPOOL> %d#, Preparing to push into pool, owning connection %d#, pooledCount=%d\n", ObjectID, 0, _pooledCount);
732 _owningObject.Target = null; // NOTE: doing this and checking for InternalError.PooledObjectHasOwner degrades the close by 2%
735 internal void PostPop(object newOwner) {
736 // Called by DbConnectionPool right after it pulls this from it's pool, we
737 // take this opportunity to ensure ownership and pool counts are legit.
739 Debug.Assert(!IsEmancipated,"pooled object not in pool");
741 // SQLBUDT #356871 -- When another thread is clearing this pool, it
742 // will doom all connections in this pool without prejudice which
743 // causes the following assert to fire, which really mucks up stress
744 // against checked bits. The assert is benign, so we're commenting
746 //Debug.Assert(CanBePooled, "pooled object is not poolable");
748 // IMPORTANT NOTE: You must have taken a lock on the object before
749 // you call this method to prevent race conditions with Clear and
750 // ReclaimEmancipatedObjects.
752 if (null != _owningObject.Target) {
753 throw ADP.InternalError(ADP.InternalErrorCode.PooledObjectHasOwner); // pooled connection already has an owner!
755 _owningObject.Target = newOwner;
757 if (Bid.IsOn(DbConnectionPool.PoolerTracePoints)) {
758 //DbConnection x = (newOwner as DbConnection);
759 Bid.PoolerTrace("<prov.DbConnectionInternal.PostPop|RES|CPOOL> %d#, Preparing to pop from pool, owning connection %d#, pooledCount=%d\n", ObjectID, 0, _pooledCount);
761 //3 // The following tests are retail assertions of things we can't allow to happen.
763 if (0 != _pooledCount) {
764 throw ADP.InternalError(ADP.InternalErrorCode.PooledObjectInPoolMoreThanOnce); // popping object off stack with multiple pooledCount
767 else if (-1 != _pooledCount) {
768 throw ADP.InternalError(ADP.InternalErrorCode.NonPooledObjectUsedMoreThanOnce); // popping object off stack with multiple pooledCount
772 internal void RemoveWeakReference(object value) {
773 DbReferenceCollection referenceCollection = ReferenceCollection;
774 if (null != referenceCollection) {
775 referenceCollection.Remove(value);
779 // Cleanup connection's transaction-specific structures (currently used by Delegated transaction).
780 // This is a separate method because cleanup can be triggered in multiple ways for a delegated
782 virtual protected void CleanupTransactionOnCompletion(SysTx.Transaction transaction) {
785 internal void DetachCurrentTransactionIfEnded() {
786 SysTx.Transaction enlistedTransaction = EnlistedTransaction;
787 if (enlistedTransaction != null) {
788 bool transactionIsDead;
790 transactionIsDead = (SysTx.TransactionStatus.Active != enlistedTransaction.TransactionInformation.Status);
792 catch (SysTx.TransactionException) {
793 // If the transaction is being processed (i.e. is part way through a rollback\commit\etc then TransactionInformation.Status will throw an exception)
794 transactionIsDead = true;
796 if (transactionIsDead) {
797 DetachTransaction(enlistedTransaction, true);
802 // Detach transaction from connection.
803 internal void DetachTransaction(SysTx.Transaction transaction, bool isExplicitlyReleasing) {
804 Bid.Trace("<prov.DbConnectionInternal.DetachTransaction|RES|CPOOL> %d#, Transaction Completed. (pooledCount=%d)\n", ObjectID, _pooledCount);
806 // potentially a multi-threaded event, so lock the connection to make sure we don't enlist in a new
807 // transaction between compare and assignment. No need to short circuit outside of lock, since failed comparisons should
808 // be the exception, not the rule.
810 // Detach if detach-on-end behavior, or if outer connection was closed
811 DbConnection owner = (DbConnection)Owner;
812 if (isExplicitlyReleasing || UnbindOnTransactionCompletion || null == owner) {
813 SysTx.Transaction currentEnlistedTransaction = _enlistedTransaction;
814 if (currentEnlistedTransaction != null && transaction.Equals(currentEnlistedTransaction)) {
816 EnlistedTransaction = null;
818 if (IsTxRootWaitingForTxEnd) {
819 DelegatedTransactionEnded();
826 // Handle transaction detach, pool cleanup and other post-transaction cleanup tasks associated with
827 internal void CleanupConnectionOnTransactionCompletion(SysTx.Transaction transaction) {
828 DetachTransaction(transaction, false);
830 DbConnectionPool pool = Pool;
832 pool.TransactionEnded(transaction, this);
836 void TransactionCompletedEvent(object sender, SysTx.TransactionEventArgs e) {
837 SysTx.Transaction transaction = e.Transaction;
839 Bid.Trace("<prov.DbConnectionInternal.TransactionCompletedEvent|RES|CPOOL> %d#, Transaction Completed. (pooledCount=%d)\n", ObjectID, _pooledCount);
841 CleanupTransactionOnCompletion(transaction);
843 CleanupConnectionOnTransactionCompletion(transaction);
848 [SecurityPermission(SecurityAction.Assert, Flags=SecurityPermissionFlag.UnmanagedCode)]
849 private void TransactionOutcomeEnlist(SysTx.Transaction transaction) {
850 transaction.TransactionCompleted += new SysTx.TransactionCompletedEventHandler(TransactionCompletedEvent);
853 internal void SetInStasis() {
855 Bid.PoolerTrace("<prov.DbConnectionInternal.SetInStasis|RES|CPOOL> %d#, Non-Pooled Connection has Delegated Transaction, waiting to Dispose.\n", ObjectID);
857 PerformanceCounters.NumberOfStasisConnections.Increment();
861 private void TerminateStasis(bool returningToPool) {
862 if (returningToPool) {
863 Bid.PoolerTrace("<prov.DbConnectionInternal.TerminateStasis|RES|CPOOL> %d#, Delegated Transaction has ended, connection is closed. Returning to general pool.\n", ObjectID);
866 Bid.PoolerTrace("<prov.DbConnectionInternal.TerminateStasis|RES|CPOOL> %d#, Delegated Transaction has ended, connection is closed/leaked. Disposing.\n", ObjectID);
869 PerformanceCounters.NumberOfStasisConnections.Decrement();
875 /// When overridden in a derived class, will check if the underlying connection is still actually alive
877 /// <param name="throwOnException">If true an exception will be thrown if the connection is dead instead of returning true\false
878 /// (this allows the caller to have the real reason that the connection is not alive (e.g. network error, etc))</param>
879 /// <returns>True if the connection is still alive, otherwise false (If not overridden, then always true)</returns>
880 internal virtual bool IsConnectionAlive(bool throwOnException = false)