| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240 |
- //------------------------------------------------------------------------------
- // <copyright file="TdsParserSessionPool.cs" company="Microsoft">
- // Copyright (c) Microsoft Corporation. All rights reserved.
- // </copyright>
- // <owner current="true" primary="true">[....]</owner>
- // <owner current="true" primary="false">[....]</owner>
- //------------------------------------------------------------------------------
- namespace System.Data.SqlClient {
- using System;
- using System.Collections.Generic;
- using System.Data.Common;
- using System.Data.ProviderBase;
- using System.Diagnostics;
- using System.Runtime.CompilerServices;
- using System.Runtime.InteropServices;
- using System.Threading;
- using System.Collections.Concurrent;
- internal class TdsParserSessionPool {
- // NOTE: This is a very simplistic, lightweight pooler. It wasn't
- // intended to handle huge number of items, just to keep track
- // of the session objects to ensure that they're cleaned up in
- // a timely manner, to avoid holding on to an unacceptible
- // amount of server-side resources in the event that consumers
- // let their data readers be GC'd, instead of explicitly
- // closing or disposing of them
-
- private const int MaxInactiveCount = 10; // pick something, preferably small...
-
- private static int _objectTypeCount; // Bid counter
- private readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount);
- private readonly TdsParser _parser; // parser that owns us
- private readonly List<TdsParserStateObject> _cache; // collection of all known sessions
- private int _cachedCount; // lock-free _cache.Count
- private TdsParserStateObject[] _freeStateObjects; // collection of all sessions available for reuse
- private int _freeStateObjectCount; // Number of available free sessions
-
- internal TdsParserSessionPool(TdsParser parser) {
- _parser = parser;
- _cache = new List<TdsParserStateObject>();
- _freeStateObjects = new TdsParserStateObject[MaxInactiveCount];
- _freeStateObjectCount = 0;
- if (Bid.AdvancedOn) {
- Bid.Trace("<sc.TdsParserSessionPool.ctor|ADV> %d# created session pool for parser %d\n", ObjectID, parser.ObjectID);
- }
- }
- private bool IsDisposed {
- get {
- return (null == _freeStateObjects);
- }
- }
- internal int ObjectID {
- get {
- return _objectID;
- }
- }
- internal void Deactivate() {
- // When being deactivated, we check all the sessions in the
- // cache to make sure they're cleaned up and then we dispose of
- // sessions that are past what we want to keep around.
- IntPtr hscp;
- Bid.ScopeEnter(out hscp, "<sc.TdsParserSessionPool.Deactivate|ADV> %d# deactivating cachedCount=%d\n", ObjectID, _cachedCount);
- try {
- lock(_cache) {
- // NOTE: The PutSession call below may choose to remove the
- // session from the cache, which will throw off our
- // enumerator. We avoid that by simply indexing backward
- // through the array.
- for (int i = _cache.Count - 1; i >= 0 ; i--) {
- TdsParserStateObject session = _cache[i];
-
- if (null != session) {
- if (session.IsOrphaned) {
- //
-
- if (Bid.AdvancedOn) {
- Bid.Trace("<sc.TdsParserSessionPool.Deactivate|ADV> %d# reclaiming session %d\n", ObjectID, session.ObjectID);
- }
- PutSession(session);
- }
- }
- }
- //
- }
- }
- finally {
- Bid.ScopeLeave(ref hscp);
- }
- }
- // This is called from a ThreadAbort - ensure that it can be run from a CER Catch
- internal void BestEffortCleanup() {
- for (int i = 0; i < _cache.Count; i++) {
- TdsParserStateObject session = _cache[i];
- if (null != session) {
- var sessionHandle = session.Handle;
- if (sessionHandle != null) {
- sessionHandle.Dispose();
- }
- }
- }
- }
- internal void Dispose() {
- if (Bid.AdvancedOn) {
- Bid.Trace("<sc.TdsParserSessionPool.Dispose|ADV> %d# disposing cachedCount=%d\n", ObjectID, _cachedCount);
- }
- lock(_cache) {
- // Dispose free sessions
- for (int i = 0; i < _freeStateObjectCount; i++) {
- if (_freeStateObjects[i] != null) {
- _freeStateObjects[i].Dispose();
- }
- }
- _freeStateObjects = null;
- _freeStateObjectCount = 0;
- // Dispose orphaned sessions
- for (int i = 0; i < _cache.Count; i++) {
- if (_cache[i] != null) {
- if (_cache[i].IsOrphaned) {
- _cache[i].Dispose();
- }
- else {
- // Remove the "initial" callback (this will allow the stateObj to be GC collected if need be)
- _cache[i].DecrementPendingCallbacks(false);
- }
- }
- }
- _cache.Clear();
- _cachedCount = 0;
- // Any active sessions will take care of themselves
- // (It's too dangerous to dispose them, as this can cause AVs)
- }
- }
- internal TdsParserStateObject GetSession(object owner) {
- TdsParserStateObject session;
- lock (_cache) {
- if (IsDisposed) {
- throw ADP.ClosedConnectionError();
- }
- else if (_freeStateObjectCount > 0) {
- // Free state object - grab it
- _freeStateObjectCount--;
- session = _freeStateObjects[_freeStateObjectCount];
- _freeStateObjects[_freeStateObjectCount] = null;
- Debug.Assert(session != null, "There was a null session in the free session list?");
- }
- else {
- // No free objects, create a new one
- session = _parser.CreateSession();
- if (Bid.AdvancedOn) {
- Bid.Trace("<sc.TdsParserSessionPool.CreateSession|ADV> %d# adding session %d to pool\n", ObjectID, session.ObjectID);
- }
- _cache.Add(session);
- _cachedCount = _cache.Count;
- }
- session.Activate(owner);
- }
- if (Bid.AdvancedOn) {
- Bid.Trace("<sc.TdsParserSessionPool.GetSession|ADV> %d# using session %d\n", ObjectID, session.ObjectID);
- }
- return session;
- }
- internal void PutSession(TdsParserStateObject session) {
- Debug.Assert (null != session, "null session?");
- //Debug.Assert(null != session.Owner, "session without owner?");
- bool okToReuse = session.Deactivate();
- lock (_cache) {
- if (IsDisposed) {
- // We're diposed - just clean out the session
- Debug.Assert(_cachedCount == 0, "SessionPool is disposed, but there are still sessions in the cache?");
- session.Dispose();
- }
- else if ((okToReuse) && (_freeStateObjectCount < MaxInactiveCount)) {
- // Session is good to re-use and our cache has space
- if (Bid.AdvancedOn) {
- Bid.Trace("<sc.TdsParserSessionPool.PutSession|ADV> %d# keeping session %d cachedCount=%d\n", ObjectID, session.ObjectID, _cachedCount);
- }
- Debug.Assert(!session._pendingData, "pending data on a pooled session?");
- _freeStateObjects[_freeStateObjectCount] = session;
- _freeStateObjectCount++;
- }
- else {
- // Either the session is bad, or we have no cache space - so dispose the session and remove it
- if (Bid.AdvancedOn) {
- Bid.Trace("<sc.TdsParserSessionPool.PutSession|ADV> %d# disposing session %d cachedCount=%d\n", ObjectID, session.ObjectID, _cachedCount);
- }
-
- bool removed = _cache.Remove(session);
- Debug.Assert(removed, "session not in pool?");
- _cachedCount = _cache.Count;
- session.Dispose();
- }
- session.RemoveOwner();
- }
- }
- internal string TraceString() {
- return String.Format(/*IFormatProvider*/ null,
- "(ObjID={0}, free={1}, cached={2}, total={3})",
- _objectID,
- null == _freeStateObjects ? "(null)" : _freeStateObjectCount.ToString((IFormatProvider) null),
- _cachedCount,
- _cache.Count);
- }
- internal int ActiveSessionsCount {
- get {
- return _cachedCount - _freeStateObjectCount;
- }
- }
- }
- }
|