DbConnectionInternal.cs 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885
  1. //------------------------------------------------------------------------------
  2. // <copyright file="DbConnectionInternal.cs" company="Microsoft">
  3. // Copyright (c) Microsoft Corporation. All rights reserved.
  4. // </copyright>
  5. // <owner current="true" primary="true">[....]</owner>
  6. // <owner current="true" primary="false">[....]</owner>
  7. //------------------------------------------------------------------------------
  8. namespace System.Data.ProviderBase {
  9. using System;
  10. using System.ComponentModel;
  11. using System.Data;
  12. using System.Data.Common;
  13. using System.Diagnostics;
  14. using System.Globalization;
  15. using System.Runtime.ConstrainedExecution;
  16. using System.Runtime.InteropServices;
  17. using System.Runtime.InteropServices.ComTypes;
  18. using System.Security;
  19. using System.Security.Permissions;
  20. using System.Threading;
  21. using System.Threading.Tasks;
  22. using SysTx = System.Transactions;
  23. internal abstract class DbConnectionInternal { // V1.1.3300
  24. private static int _objectTypeCount;
  25. internal readonly int _objectID = Interlocked.Increment(ref _objectTypeCount);
  26. internal static readonly StateChangeEventArgs StateChangeClosed = new StateChangeEventArgs(ConnectionState.Open, ConnectionState.Closed);
  27. internal static readonly StateChangeEventArgs StateChangeOpen = new StateChangeEventArgs(ConnectionState.Closed, ConnectionState.Open);
  28. private readonly bool _allowSetConnectionString;
  29. private readonly bool _hidePassword;
  30. private readonly ConnectionState _state;
  31. 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)
  32. private DbConnectionPool _connectionPool; // the pooler that the connection came from (Pooled connections only)
  33. private DbConnectionPoolCounters _performanceCounters; // the performance counters we're supposed to update
  34. private DbReferenceCollection _referenceCollection; // collection of objects that we need to notify in some way when we're being deactivated
  35. 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)
  36. private bool _connectionIsDoomed; // true when the connection should no longer be used.
  37. private bool _cannotBePooled; // true when the connection should no longer be pooled.
  38. private bool _isInStasis;
  39. private DateTime _createTime; // when the connection was created.
  40. private SysTx.Transaction _enlistedTransaction; // [usage must be thread-safe] the transaction that we're enlisted in, either manually or automatically
  41. // _enlistedTransaction is a clone, so that transaction information can be queried even if the original transaction object is disposed.
  42. // However, there are times when we need to know if the original transaction object was disposed, so we keep a reference to it here.
  43. // This field should only be assigned a value at the same time _enlistedTransaction is updated.
  44. // Also, this reference should not be disposed, since we aren't taking ownership of it.
  45. private SysTx.Transaction _enlistedTransactionOriginal;
  46. #if DEBUG
  47. private int _activateCount; // debug only counter to verify activate/deactivates are in [....].
  48. #endif //DEBUG
  49. protected DbConnectionInternal() : this(ConnectionState.Open, true, false) { // V1.1.3300
  50. }
  51. // Constructor for internal connections
  52. internal DbConnectionInternal(ConnectionState state, bool hidePassword, bool allowSetConnectionString) {
  53. _allowSetConnectionString = allowSetConnectionString;
  54. _hidePassword = hidePassword;
  55. _state = state;
  56. }
  57. internal bool AllowSetConnectionString {
  58. get {
  59. return _allowSetConnectionString;
  60. }
  61. }
  62. internal bool CanBePooled {
  63. get {
  64. bool flag = (!_connectionIsDoomed && !_cannotBePooled && !_owningObject.IsAlive);
  65. return flag;
  66. }
  67. }
  68. protected internal SysTx.Transaction EnlistedTransaction {
  69. get {
  70. return _enlistedTransaction;
  71. }
  72. set {
  73. SysTx.Transaction currentEnlistedTransaction = _enlistedTransaction;
  74. if (((null == currentEnlistedTransaction) && (null != value))
  75. || ((null != currentEnlistedTransaction) && !currentEnlistedTransaction.Equals(value))) { // WebData 20000024
  76. // Pay attention to the order here:
  77. // 1) defect from any notifications
  78. // 2) replace the transaction
  79. // 3) re-enlist in notifications for the new transaction
  80. // SQLBUDT #230558 we need to use a clone of the transaction
  81. // when we store it, or we'll end up keeping it past the
  82. // duration of the using block of the TransactionScope
  83. SysTx.Transaction valueClone = null;
  84. SysTx.Transaction previousTransactionClone = null;
  85. try {
  86. if (null != value) {
  87. valueClone = value.Clone();
  88. }
  89. // NOTE: rather than take locks around several potential round-
  90. // trips to the server, and/or virtual function calls, we simply
  91. // presume that you aren't doing something illegal from multiple
  92. // threads, and check once we get around to finalizing things
  93. // inside a lock.
  94. lock(this) {
  95. // NOTE: There is still a race condition here, when we are
  96. // called from EnlistTransaction (which cannot re-enlist)
  97. // instead of EnlistDistributedTransaction (which can),
  98. // however this should have been handled by the outer
  99. // connection which checks to ensure that it's OK. The
  100. // only case where we have the race condition is multiple
  101. // concurrent enlist requests to the same connection, which
  102. // is a bit out of line with something we should have to
  103. // support.
  104. // enlisted transaction can be nullified in Dispose call without lock
  105. previousTransactionClone = Interlocked.Exchange(ref _enlistedTransaction, valueClone);
  106. _enlistedTransactionOriginal = value;
  107. value = valueClone;
  108. valueClone = null; // we've stored it, don't dispose it.
  109. }
  110. }
  111. finally {
  112. // we really need to dispose our clones; they may have
  113. // native resources and GC may not happen soon enough.
  114. // VSDevDiv 479564: don't dispose if still holding reference in _enlistedTransaction
  115. if (null != previousTransactionClone &&
  116. !Object.ReferenceEquals(previousTransactionClone, _enlistedTransaction)) {
  117. previousTransactionClone.Dispose();
  118. }
  119. if (null != valueClone && !Object.ReferenceEquals(valueClone, _enlistedTransaction)) {
  120. valueClone.Dispose();
  121. }
  122. }
  123. // I don't believe that we need to lock to protect the actual
  124. // enlistment in the transaction; it would only protect us
  125. // against multiple concurrent calls to enlist, which really
  126. // isn't supported anyway.
  127. if (null != value) {
  128. if (Bid.IsOn(DbConnectionPool.PoolerTracePoints)) {
  129. int x = value.GetHashCode();
  130. Bid.PoolerTrace("<prov.DbConnectionInternal.set_EnlistedTransaction|RES|CPOOL> %d#, Transaction %d#, Enlisting.\n", ObjectID, x);
  131. }
  132. TransactionOutcomeEnlist(value);
  133. }
  134. }
  135. }
  136. }
  137. /// <summary>
  138. /// Get boolean value that indicates whether the enlisted transaction has been disposed.
  139. /// </summary>
  140. /// <value>
  141. /// True if there is an enlisted transaction, and it has been diposed.
  142. /// False if there is an enlisted transaction that has not been disposed, or if the transaction reference is null.
  143. /// </value>
  144. /// <remarks>
  145. /// This method must be called while holding a lock on the DbConnectionInternal instance.
  146. /// </remarks>
  147. protected bool EnlistedTransactionDisposed
  148. {
  149. get
  150. {
  151. // Until the Transaction.Disposed property is public it is necessary to access a member
  152. // that throws if the object is disposed to determine if in fact the transaction is disposed.
  153. try
  154. {
  155. bool disposed;
  156. SysTx.Transaction currentEnlistedTransactionOriginal = _enlistedTransactionOriginal;
  157. if (currentEnlistedTransactionOriginal != null)
  158. {
  159. disposed = currentEnlistedTransactionOriginal.TransactionInformation == null;
  160. }
  161. else
  162. {
  163. // Don't expect to get here in the general case,
  164. // Since this getter is called by CheckEnlistedTransactionBinding
  165. // after checking for a non-null enlisted transaction (and it does so under lock).
  166. disposed = false;
  167. }
  168. return disposed;
  169. }
  170. catch (ObjectDisposedException)
  171. {
  172. return true;
  173. }
  174. }
  175. }
  176. // Is this connection in stasis, waiting for transaction to end before returning to pool?
  177. internal bool IsTxRootWaitingForTxEnd {
  178. get {
  179. return _isInStasis;
  180. }
  181. }
  182. /// <summary>
  183. /// Get boolean that specifies whether an enlisted transaction can be unbound from
  184. /// the connection when that transaction completes.
  185. /// </summary>
  186. /// <value>
  187. /// True if the enlisted transaction can be unbound on transaction completion; otherwise false.
  188. /// </value>
  189. virtual protected bool UnbindOnTransactionCompletion
  190. {
  191. get
  192. {
  193. return true;
  194. }
  195. }
  196. // Is this a connection that must be put in stasis (or is already in stasis) pending the end of it's transaction?
  197. virtual protected internal bool IsNonPoolableTransactionRoot {
  198. get {
  199. return false; // if you want to have delegated transactions that are non-poolable, you better override this...
  200. }
  201. }
  202. virtual internal bool IsTransactionRoot {
  203. get {
  204. return false; // if you want to have delegated transactions, you better override this...
  205. }
  206. }
  207. protected internal bool IsConnectionDoomed {
  208. get {
  209. return _connectionIsDoomed;
  210. }
  211. }
  212. internal bool IsEmancipated {
  213. get {
  214. // NOTE: There are race conditions between PrePush, PostPop and this
  215. // property getter -- only use this while this object is locked;
  216. // (DbConnectionPool.Clear and ReclaimEmancipatedObjects
  217. // do this for us)
  218. // Remember how this works (I keep getting confused...)
  219. //
  220. // _pooledCount is incremented when the connection is pushed into the pool
  221. // _pooledCount is decremented when the connection is popped from the pool
  222. // _pooledCount is set to -1 when the connection is not pooled (just in case...)
  223. //
  224. // That means that:
  225. //
  226. // _pooledCount > 1 connection is in the pool multiple times (this is a serious bug...)
  227. // _pooledCount == 1 connection is in the pool
  228. // _pooledCount == 0 connection is out of the pool
  229. // _pooledCount == -1 connection is not a pooled connection; we shouldn't be here for non-pooled connections.
  230. // _pooledCount < -1 connection out of the pool multiple times (not sure how this could happen...)
  231. //
  232. // Now, our job is to return TRUE when the connection is out
  233. // of the pool and it's owning object is no longer around to
  234. // return it.
  235. bool value = !IsTxRootWaitingForTxEnd && (_pooledCount < 1) && !_owningObject.IsAlive;
  236. return value;
  237. }
  238. }
  239. internal bool IsInPool {
  240. get {
  241. Debug.Assert(_pooledCount <= 1 && _pooledCount >= -1, "Pooled count for object is invalid");
  242. return (_pooledCount == 1);
  243. }
  244. }
  245. internal int ObjectID {
  246. get {
  247. return _objectID;
  248. }
  249. }
  250. protected internal object Owner {
  251. // We use a weak reference to the owning object so we can identify when
  252. // it has been garbage collected without thowing exceptions.
  253. get {
  254. return _owningObject.Target;
  255. }
  256. }
  257. internal DbConnectionPool Pool {
  258. get {
  259. return _connectionPool;
  260. }
  261. }
  262. protected DbConnectionPoolCounters PerformanceCounters {
  263. get {
  264. return _performanceCounters;
  265. }
  266. }
  267. virtual protected bool ReadyToPrepareTransaction {
  268. get {
  269. return true;
  270. }
  271. }
  272. protected internal DbReferenceCollection ReferenceCollection {
  273. get {
  274. return _referenceCollection;
  275. }
  276. }
  277. abstract public string ServerVersion {
  278. get;
  279. }
  280. // this should be abstract but untill it is added to all the providers virtual will have to do [....]
  281. virtual public string ServerVersionNormalized {
  282. get{
  283. throw ADP.NotSupported();
  284. }
  285. }
  286. public bool ShouldHidePassword {
  287. get {
  288. return _hidePassword;
  289. }
  290. }
  291. public ConnectionState State {
  292. get {
  293. return _state;
  294. }
  295. }
  296. abstract protected void Activate(SysTx.Transaction transaction);
  297. internal void ActivateConnection(SysTx.Transaction transaction) {
  298. // Internal method called from the connection pooler so we don't expose
  299. // the Activate method publicly.
  300. Bid.PoolerTrace("<prov.DbConnectionInternal.ActivateConnection|RES|INFO|CPOOL> %d#, Activating\n", ObjectID);
  301. #if DEBUG
  302. int activateCount = Interlocked.Increment(ref _activateCount);
  303. Debug.Assert(1 == activateCount, "activated multiple times?");
  304. #endif // DEBUG
  305. Activate(transaction);
  306. #if !MOBILE
  307. PerformanceCounters.NumberOfActiveConnections.Increment();
  308. #endif
  309. }
  310. internal void AddWeakReference(object value, int tag) {
  311. if (null == _referenceCollection) {
  312. _referenceCollection = CreateReferenceCollection();
  313. if (null == _referenceCollection) {
  314. throw ADP.InternalError(ADP.InternalErrorCode.CreateReferenceCollectionReturnedNull);
  315. }
  316. }
  317. _referenceCollection.Add(value, tag);
  318. }
  319. abstract public DbTransaction BeginTransaction(IsolationLevel il);
  320. virtual public void ChangeDatabase(string value) {
  321. throw ADP.MethodNotImplemented("ChangeDatabase");
  322. }
  323. internal virtual void CloseConnection(DbConnection owningObject, DbConnectionFactory connectionFactory) {
  324. // The implementation here is the implementation required for the
  325. // "open" internal connections, since our own private "closed"
  326. // singleton internal connection objects override this method to
  327. // prevent anything funny from happening (like disposing themselves
  328. // or putting them into a connection pool)
  329. //
  330. // Derived class should override DbConnectionInternal.Deactivate and DbConnectionInternal.Dispose
  331. // for cleaning up after DbConnection.Close
  332. // protected override void Deactivate() { // override DbConnectionInternal.Close
  333. // // do derived class connection deactivation for both pooled & non-pooled connections
  334. // }
  335. // public override void Dispose() { // override DbConnectionInternal.Close
  336. // // do derived class cleanup
  337. // base.Dispose();
  338. // }
  339. //
  340. // overriding DbConnection.Close is also possible, but must provider for their own synchronization
  341. // public override void Close() { // override DbConnection.Close
  342. // base.Close();
  343. // // do derived class outer connection for both pooled & non-pooled connections
  344. // // user must do their own synchronization here
  345. // }
  346. //
  347. // if the DbConnectionInternal derived class needs to close the connection it should
  348. // delegate to the DbConnection if one exists or directly call dispose
  349. // DbConnection owningObject = (DbConnection)Owner;
  350. // if (null != owningObject) {
  351. // owningObject.Close(); // force the closed state on the outer object.
  352. // }
  353. // else {
  354. // Dispose();
  355. // }
  356. //
  357. ////////////////////////////////////////////////////////////////
  358. // DON'T MESS WITH THIS CODE UNLESS YOU KNOW WHAT YOU'RE DOING!
  359. ////////////////////////////////////////////////////////////////
  360. Debug.Assert(null != owningObject, "null owningObject");
  361. Debug.Assert(null != connectionFactory, "null connectionFactory");
  362. Bid.PoolerTrace("<prov.DbConnectionInternal.CloseConnection|RES|CPOOL> %d# Closing.\n", ObjectID);
  363. // if an exception occurs after the state change but before the try block
  364. // the connection will be stuck in OpenBusy state. The commented out try-catch
  365. // block doesn't really help because a ThreadAbort during the finally block
  366. // would just refert the connection to a bad state.
  367. // Open->Closed: guarantee internal connection is returned to correct pool
  368. if (connectionFactory.SetInnerConnectionFrom(owningObject, DbConnectionOpenBusy.SingletonInstance, this)) {
  369. // Lock to prevent race condition with cancellation
  370. lock (this) {
  371. object lockToken = ObtainAdditionalLocksForClose();
  372. try {
  373. PrepareForCloseConnection();
  374. DbConnectionPool connectionPool = Pool;
  375. // Detach from enlisted transactions that are no longer active on close
  376. DetachCurrentTransactionIfEnded();
  377. // The singleton closed classes won't have owners and
  378. // connection pools, and we won't want to put them back
  379. // into the pool.
  380. if (null != connectionPool) {
  381. connectionPool.PutObject(this, owningObject); // PutObject calls Deactivate for us...
  382. // NOTE: Before we leave the PutObject call, another
  383. // thread may have already popped the connection from
  384. // the pool, so don't expect to be able to verify it.
  385. }
  386. else {
  387. Deactivate(); // ensure we de-activate non-pooled connections, or the data readers and transactions may not get cleaned up...
  388. #if !MOBILE
  389. PerformanceCounters.HardDisconnectsPerSecond.Increment();
  390. #endif
  391. // To prevent an endless recursion, we need to clear
  392. // the owning object before we call dispose so that
  393. // we can't get here a second time... Ordinarily, I
  394. // would call setting the owner to null a hack, but
  395. // this is safe since we're about to dispose the
  396. // object and it won't have an owner after that for
  397. // certain.
  398. _owningObject.Target = null;
  399. if (IsTransactionRoot) {
  400. SetInStasis();
  401. }
  402. else {
  403. #if MONO_PARTIAL_DATA_IMPORT
  404. Dispose();
  405. #else
  406. #if !MOBILE
  407. PerformanceCounters.NumberOfNonPooledConnections.Decrement();
  408. #endif
  409. if (this.GetType() != typeof(System.Data.SqlClient.SqlInternalConnectionSmi))
  410. {
  411. Dispose();
  412. }
  413. #endif
  414. }
  415. }
  416. }
  417. finally {
  418. ReleaseAdditionalLocksForClose(lockToken);
  419. // if a ThreadAbort puts us here then its possible the outer connection will not reference
  420. // this and this will be orphaned, not reclaimed by object pool until outer connection goes out of scope.
  421. connectionFactory.SetInnerConnectionEvent(owningObject, DbConnectionClosedPreviouslyOpened.SingletonInstance);
  422. }
  423. }
  424. }
  425. }
  426. virtual internal void PrepareForReplaceConnection() {
  427. // By default, there is no preperation required
  428. }
  429. virtual protected void PrepareForCloseConnection() {
  430. // By default, there is no preperation required
  431. }
  432. virtual protected object ObtainAdditionalLocksForClose() {
  433. return null; // no additional locks in default implementation
  434. }
  435. virtual protected void ReleaseAdditionalLocksForClose(object lockToken) {
  436. // no additional locks in default implementation
  437. }
  438. virtual protected DbReferenceCollection CreateReferenceCollection() {
  439. throw ADP.InternalError(ADP.InternalErrorCode.AttemptingToConstructReferenceCollectionOnStaticObject);
  440. }
  441. abstract protected void Deactivate();
  442. internal void DeactivateConnection() {
  443. // Internal method called from the connection pooler so we don't expose
  444. // the Deactivate method publicly.
  445. Bid.PoolerTrace("<prov.DbConnectionInternal.DeactivateConnection|RES|INFO|CPOOL> %d#, Deactivating\n", ObjectID);
  446. #if DEBUG
  447. int activateCount = Interlocked.Decrement(ref _activateCount);
  448. Debug.Assert(0 == activateCount, "activated multiple times?");
  449. #endif // DEBUG
  450. #if !MOBILE
  451. if (PerformanceCounters != null) { // Pool.Clear will DestroyObject that will clean performanceCounters before going here
  452. PerformanceCounters.NumberOfActiveConnections.Decrement();
  453. }
  454. #endif
  455. if (!_connectionIsDoomed && Pool.UseLoadBalancing) {
  456. // If we're not already doomed, check the connection's lifetime and
  457. // doom it if it's lifetime has elapsed.
  458. DateTime now = DateTime.UtcNow; // WebData 111116
  459. if ((now.Ticks - _createTime.Ticks) > Pool.LoadBalanceTimeout.Ticks) {
  460. DoNotPoolThisConnection();
  461. }
  462. }
  463. Deactivate();
  464. }
  465. virtual internal void DelegatedTransactionEnded() {
  466. // Called by System.Transactions when the delegated transaction has
  467. // completed. We need to make closed connections that are in stasis
  468. // available again, or disposed closed/leaked non-pooled connections.
  469. // IMPORTANT NOTE: You must have taken a lock on the object before
  470. // you call this method to prevent race conditions with Clear and
  471. // ReclaimEmancipatedObjects.
  472. Bid.Trace("<prov.DbConnectionInternal.DelegatedTransactionEnded|RES|CPOOL> %d#, Delegated Transaction Completed.\n", ObjectID);
  473. if (1 == _pooledCount) {
  474. // When _pooledCount is 1, it indicates a closed, pooled,
  475. // connection so it is ready to put back into the pool for
  476. // general use.
  477. TerminateStasis(true);
  478. Deactivate(); // call it one more time just in case
  479. DbConnectionPool pool = Pool;
  480. if (null == pool) {
  481. throw ADP.InternalError(ADP.InternalErrorCode.PooledObjectWithoutPool); // pooled connection does not have a pool
  482. }
  483. pool.PutObjectFromTransactedPool(this);
  484. }
  485. else if (-1 == _pooledCount && !_owningObject.IsAlive) {
  486. // When _pooledCount is -1 and the owning object no longer exists,
  487. // it indicates a closed (or leaked), non-pooled connection so
  488. // it is safe to dispose.
  489. TerminateStasis(false);
  490. Deactivate(); // call it one more time just in case
  491. // it's a non-pooled connection, we need to dispose of it
  492. // once and for all, or the server will have fits about us
  493. // leaving connections open until the client-side GC kicks
  494. // in.
  495. #if !MOBILE
  496. PerformanceCounters.NumberOfNonPooledConnections.Decrement();
  497. #endif
  498. Dispose();
  499. }
  500. // When _pooledCount is 0, the connection is a pooled connection
  501. // that is either open (if the owning object is alive) or leaked (if
  502. // the owning object is not alive) In either case, we can't muck
  503. // with the connection here.
  504. }
  505. public virtual void Dispose()
  506. {
  507. _connectionPool = null;
  508. _performanceCounters = null;
  509. _connectionIsDoomed = true;
  510. _enlistedTransactionOriginal = null; // should not be disposed
  511. // Dispose of the _enlistedTransaction since it is a clone
  512. // of the original reference.
  513. // VSDD 780271 - _enlistedTransaction can be changed by another thread (TX end event)
  514. SysTx.Transaction enlistedTransaction = Interlocked.Exchange(ref _enlistedTransaction, null);
  515. if (enlistedTransaction != null)
  516. {
  517. enlistedTransaction.Dispose();
  518. }
  519. }
  520. protected internal void DoNotPoolThisConnection() {
  521. _cannotBePooled = true;
  522. Bid.PoolerTrace("<prov.DbConnectionInternal.DoNotPoolThisConnection|RES|INFO|CPOOL> %d#, Marking pooled object as non-poolable so it will be disposed\n", ObjectID);
  523. }
  524. /// <devdoc>Ensure that this connection cannot be put back into the pool.</devdoc>
  525. [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
  526. protected internal void DoomThisConnection() {
  527. _connectionIsDoomed = true;
  528. Bid.PoolerTrace("<prov.DbConnectionInternal.DoomThisConnection|RES|INFO|CPOOL> %d#, Dooming\n", ObjectID);
  529. }
  530. abstract public void EnlistTransaction(SysTx.Transaction transaction);
  531. virtual protected internal DataTable GetSchema(DbConnectionFactory factory, DbConnectionPoolGroup poolGroup, DbConnection outerConnection, string collectionName, string[] restrictions){
  532. Debug.Assert(outerConnection != null,"outerConnection may not be null.");
  533. DbMetaDataFactory metaDataFactory = factory.GetMetaDataFactory(poolGroup, this);
  534. Debug.Assert(metaDataFactory != null,"metaDataFactory may not be null.");
  535. return metaDataFactory.GetSchema(outerConnection, collectionName,restrictions);
  536. }
  537. internal void MakeNonPooledObject(object owningObject, DbConnectionPoolCounters performanceCounters) {
  538. // Used by DbConnectionFactory to indicate that this object IS NOT part of
  539. // a connection pool.
  540. _connectionPool = null;
  541. _performanceCounters = performanceCounters;
  542. _owningObject.Target = owningObject;
  543. _pooledCount = -1;
  544. }
  545. internal void MakePooledConnection(DbConnectionPool connectionPool) {
  546. // Used by DbConnectionFactory to indicate that this object IS part of
  547. // a connection pool.
  548. //
  549. _createTime = DateTime.UtcNow; // WebData 111116
  550. _connectionPool = connectionPool;
  551. _performanceCounters = connectionPool.PerformanceCounters;
  552. }
  553. internal void NotifyWeakReference(int message) {
  554. DbReferenceCollection referenceCollection = ReferenceCollection;
  555. if (null != referenceCollection) {
  556. referenceCollection.Notify(message);
  557. }
  558. }
  559. internal virtual void OpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory) {
  560. if (!TryOpenConnection(outerConnection, connectionFactory, null, null)) {
  561. throw ADP.InternalError(ADP.InternalErrorCode.SynchronousConnectReturnedPending);
  562. }
  563. }
  564. /// <devdoc>The default implementation is for the open connection objects, and
  565. /// it simply throws. Our private closed-state connection objects
  566. /// override this and do the correct thing.</devdoc>
  567. // User code should either override DbConnectionInternal.Activate when it comes out of the pool
  568. // or override DbConnectionFactory.CreateConnection when the connection is created for non-pooled connections
  569. internal virtual bool TryOpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource<DbConnectionInternal> retry, DbConnectionOptions userOptions) {
  570. throw ADP.ConnectionAlreadyOpen(State);
  571. }
  572. internal virtual bool TryReplaceConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource<DbConnectionInternal> retry, DbConnectionOptions userOptions) {
  573. throw ADP.MethodNotImplemented("TryReplaceConnection");
  574. }
  575. protected bool TryOpenConnectionInternal(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource<DbConnectionInternal> retry, DbConnectionOptions userOptions) {
  576. // ?->Connecting: prevent set_ConnectionString during Open
  577. if (connectionFactory.SetInnerConnectionFrom(outerConnection, DbConnectionClosedConnecting.SingletonInstance, this)) {
  578. DbConnectionInternal openConnection = null;
  579. try {
  580. connectionFactory.PermissionDemand(outerConnection);
  581. if (!connectionFactory.TryGetConnection(outerConnection, retry, userOptions, this, out openConnection)) {
  582. return false;
  583. }
  584. }
  585. catch {
  586. // This should occure for all exceptions, even ADP.UnCatchableExceptions.
  587. connectionFactory.SetInnerConnectionTo(outerConnection, this);
  588. throw;
  589. }
  590. if (null == openConnection) {
  591. connectionFactory.SetInnerConnectionTo(outerConnection, this);
  592. throw ADP.InternalConnectionError(ADP.ConnectionError.GetConnectionReturnsNull);
  593. }
  594. connectionFactory.SetInnerConnectionEvent(outerConnection, openConnection);
  595. }
  596. return true;
  597. }
  598. internal void PrePush(object expectedOwner) {
  599. // Called by DbConnectionPool when we're about to be put into it's pool, we
  600. // take this opportunity to ensure ownership and pool counts are legit.
  601. // IMPORTANT NOTE: You must have taken a lock on the object before
  602. // you call this method to prevent race conditions with Clear and
  603. // ReclaimEmancipatedObjects.
  604. //3 // The following tests are retail assertions of things we can't allow to happen.
  605. if (null == expectedOwner) {
  606. if (null != _owningObject.Target) {
  607. throw ADP.InternalError(ADP.InternalErrorCode.UnpooledObjectHasOwner); // new unpooled object has an owner
  608. }
  609. }
  610. else if (_owningObject.Target != expectedOwner) {
  611. throw ADP.InternalError(ADP.InternalErrorCode.UnpooledObjectHasWrongOwner); // unpooled object has incorrect owner
  612. }
  613. if (0 != _pooledCount) {
  614. throw ADP.InternalError(ADP.InternalErrorCode.PushingObjectSecondTime); // pushing object onto stack a second time
  615. }
  616. if (Bid.IsOn(DbConnectionPool.PoolerTracePoints)) {
  617. //DbConnection x = (expectedOwner as DbConnection);
  618. Bid.PoolerTrace("<prov.DbConnectionInternal.PrePush|RES|CPOOL> %d#, Preparing to push into pool, owning connection %d#, pooledCount=%d\n", ObjectID, 0, _pooledCount);
  619. }
  620. _pooledCount++;
  621. _owningObject.Target = null; // NOTE: doing this and checking for InternalError.PooledObjectHasOwner degrades the close by 2%
  622. }
  623. internal void PostPop(object newOwner) {
  624. // Called by DbConnectionPool right after it pulls this from it's pool, we
  625. // take this opportunity to ensure ownership and pool counts are legit.
  626. Debug.Assert(!IsEmancipated,"pooled object not in pool");
  627. // SQLBUDT #356871 -- When another thread is clearing this pool, it
  628. // will doom all connections in this pool without prejudice which
  629. // causes the following assert to fire, which really mucks up stress
  630. // against checked bits. The assert is benign, so we're commenting
  631. // it out.
  632. //Debug.Assert(CanBePooled, "pooled object is not poolable");
  633. // IMPORTANT NOTE: You must have taken a lock on the object before
  634. // you call this method to prevent race conditions with Clear and
  635. // ReclaimEmancipatedObjects.
  636. if (null != _owningObject.Target) {
  637. throw ADP.InternalError(ADP.InternalErrorCode.PooledObjectHasOwner); // pooled connection already has an owner!
  638. }
  639. _owningObject.Target = newOwner;
  640. _pooledCount--;
  641. if (Bid.IsOn(DbConnectionPool.PoolerTracePoints)) {
  642. //DbConnection x = (newOwner as DbConnection);
  643. Bid.PoolerTrace("<prov.DbConnectionInternal.PostPop|RES|CPOOL> %d#, Preparing to pop from pool, owning connection %d#, pooledCount=%d\n", ObjectID, 0, _pooledCount);
  644. }
  645. //3 // The following tests are retail assertions of things we can't allow to happen.
  646. if (null != Pool) {
  647. if (0 != _pooledCount) {
  648. throw ADP.InternalError(ADP.InternalErrorCode.PooledObjectInPoolMoreThanOnce); // popping object off stack with multiple pooledCount
  649. }
  650. }
  651. else if (-1 != _pooledCount) {
  652. throw ADP.InternalError(ADP.InternalErrorCode.NonPooledObjectUsedMoreThanOnce); // popping object off stack with multiple pooledCount
  653. }
  654. }
  655. internal void RemoveWeakReference(object value) {
  656. DbReferenceCollection referenceCollection = ReferenceCollection;
  657. if (null != referenceCollection) {
  658. referenceCollection.Remove(value);
  659. }
  660. }
  661. // Cleanup connection's transaction-specific structures (currently used by Delegated transaction).
  662. // This is a separate method because cleanup can be triggered in multiple ways for a delegated
  663. // transaction.
  664. virtual protected void CleanupTransactionOnCompletion(SysTx.Transaction transaction) {
  665. }
  666. internal void DetachCurrentTransactionIfEnded() {
  667. SysTx.Transaction enlistedTransaction = EnlistedTransaction;
  668. if (enlistedTransaction != null) {
  669. bool transactionIsDead;
  670. try {
  671. transactionIsDead = (SysTx.TransactionStatus.Active != enlistedTransaction.TransactionInformation.Status);
  672. }
  673. catch (SysTx.TransactionException) {
  674. // If the transaction is being processed (i.e. is part way through a rollback\commit\etc then TransactionInformation.Status will throw an exception)
  675. transactionIsDead = true;
  676. }
  677. if (transactionIsDead) {
  678. DetachTransaction(enlistedTransaction, true);
  679. }
  680. }
  681. }
  682. // Detach transaction from connection.
  683. internal void DetachTransaction(SysTx.Transaction transaction, bool isExplicitlyReleasing) {
  684. Bid.Trace("<prov.DbConnectionInternal.DetachTransaction|RES|CPOOL> %d#, Transaction Completed. (pooledCount=%d)\n", ObjectID, _pooledCount);
  685. // potentially a multi-threaded event, so lock the connection to make sure we don't enlist in a new
  686. // transaction between compare and assignment. No need to short circuit outside of lock, since failed comparisons should
  687. // be the exception, not the rule.
  688. lock (this) {
  689. // Detach if detach-on-end behavior, or if outer connection was closed
  690. DbConnection owner = (DbConnection)Owner;
  691. if (isExplicitlyReleasing || UnbindOnTransactionCompletion || null == owner) {
  692. SysTx.Transaction currentEnlistedTransaction = _enlistedTransaction;
  693. if (currentEnlistedTransaction != null && transaction.Equals(currentEnlistedTransaction)) {
  694. EnlistedTransaction = null;
  695. if (IsTxRootWaitingForTxEnd) {
  696. DelegatedTransactionEnded();
  697. }
  698. }
  699. }
  700. }
  701. }
  702. // Handle transaction detach, pool cleanup and other post-transaction cleanup tasks associated with
  703. internal void CleanupConnectionOnTransactionCompletion(SysTx.Transaction transaction) {
  704. DetachTransaction(transaction, false);
  705. DbConnectionPool pool = Pool;
  706. if (null != pool) {
  707. pool.TransactionEnded(transaction, this);
  708. }
  709. }
  710. void TransactionCompletedEvent(object sender, SysTx.TransactionEventArgs e) {
  711. SysTx.Transaction transaction = e.Transaction;
  712. Bid.Trace("<prov.DbConnectionInternal.TransactionCompletedEvent|RES|CPOOL> %d#, Transaction Completed. (pooledCount=%d)\n", ObjectID, _pooledCount);
  713. CleanupTransactionOnCompletion(transaction);
  714. CleanupConnectionOnTransactionCompletion(transaction);
  715. }
  716. //
  717. [SecurityPermission(SecurityAction.Assert, Flags=SecurityPermissionFlag.UnmanagedCode)]
  718. private void TransactionOutcomeEnlist(SysTx.Transaction transaction) {
  719. transaction.TransactionCompleted += new SysTx.TransactionCompletedEventHandler(TransactionCompletedEvent);
  720. }
  721. internal void SetInStasis() {
  722. _isInStasis = true;
  723. Bid.PoolerTrace("<prov.DbConnectionInternal.SetInStasis|RES|CPOOL> %d#, Non-Pooled Connection has Delegated Transaction, waiting to Dispose.\n", ObjectID);
  724. #if !MOBILE
  725. PerformanceCounters.NumberOfStasisConnections.Increment();
  726. #endif
  727. }
  728. private void TerminateStasis(bool returningToPool) {
  729. if (returningToPool) {
  730. Bid.PoolerTrace("<prov.DbConnectionInternal.TerminateStasis|RES|CPOOL> %d#, Delegated Transaction has ended, connection is closed. Returning to general pool.\n", ObjectID);
  731. }
  732. else {
  733. Bid.PoolerTrace("<prov.DbConnectionInternal.TerminateStasis|RES|CPOOL> %d#, Delegated Transaction has ended, connection is closed/leaked. Disposing.\n", ObjectID);
  734. }
  735. #if !MOBILE
  736. PerformanceCounters.NumberOfStasisConnections.Decrement();
  737. #endif
  738. _isInStasis = false;
  739. }
  740. /// <summary>
  741. /// When overridden in a derived class, will check if the underlying connection is still actually alive
  742. /// </summary>
  743. /// <param name="throwOnException">If true an exception will be thrown if the connection is dead instead of returning true\false
  744. /// (this allows the caller to have the real reason that the connection is not alive (e.g. network error, etc))</param>
  745. /// <returns>True if the connection is still alive, otherwise false (If not overridden, then always true)</returns>
  746. internal virtual bool IsConnectionAlive(bool throwOnException = false)
  747. {
  748. return true;
  749. }
  750. }
  751. }