Welcome to mirror list, hosted at ThFree Co, Russian Federation.

TdsParserSessionPool.cs « SqlClient « Data « System « System.Data « referencesource « class « mcs - github.com/mono/mono.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: cb4d9b10da77bacf0d18e705a046aecd30cced14 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
//------------------------------------------------------------------------------
// <copyright file="TdsParserSessionPool.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
// <owner current="true" primary="true">Microsoft</owner>
// <owner current="true" primary="false">Microsoft</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;
            }
        }
    }
}