File: DbConnectionPoolGroup.cs

package info (click to toggle)
mono 6.14.1%2Bds2-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,282,732 kB
  • sloc: cs: 11,182,461; xml: 2,850,281; ansic: 699,123; cpp: 122,919; perl: 58,604; javascript: 30,841; asm: 21,845; makefile: 19,602; sh: 10,973; python: 4,772; pascal: 925; sql: 859; sed: 16; php: 1
file content (311 lines) | stat: -rw-r--r-- 14,348 bytes parent folder | download | duplicates (6)
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
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
//------------------------------------------------------------------------------
// <copyright file="DbConnectionPoolGroup.cs" company="Microsoft">
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
// <owner current="true" primary="true">Microsoft</owner>
//------------------------------------------------------------------------------

namespace System.Data.ProviderBase {

    using System;
    using System.Collections.Concurrent;
    using System.Data.Common;
    using System.Diagnostics;
    using System.Threading;

    // set_ConnectionString calls DbConnectionFactory.GetConnectionPoolGroup
    // when not found a new pool entry is created and potentially added
    // DbConnectionPoolGroup starts in the Active state

    // Open calls DbConnectionFactory.GetConnectionPool
    // if the existing pool entry is Disabled, GetConnectionPoolGroup is called for a new entry
    // DbConnectionFactory.GetConnectionPool calls DbConnectionPoolGroup.GetConnectionPool

    // DbConnectionPoolGroup.GetConnectionPool will return pool for the current identity
    // or null if identity is restricted or pooling is disabled or state is disabled at time of add
    // state changes are Active->Active, Idle->Active

    // DbConnectionFactory.PruneConnectionPoolGroups calls Prune
    // which will QueuePoolForRelease on all empty pools
    // and once no pools remain, change state from Active->Idle->Disabled
    // Once Disabled, factory can remove its reference to the pool entry

    sealed internal class DbConnectionPoolGroup {
        private readonly DbConnectionOptions               _connectionOptions;
        private readonly DbConnectionPoolKey               _poolKey;
        private readonly DbConnectionPoolGroupOptions      _poolGroupOptions;
        private ConcurrentDictionary<DbConnectionPoolIdentity, DbConnectionPool> _poolCollection;

        private          int                               _state;          // see PoolGroupState* below

        private          DbConnectionPoolGroupProviderInfo _providerInfo;
        private          DbMetaDataFactory                 _metaDataFactory;

        private static int _objectTypeCount; // Bid counter
        internal readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount);

        // always lock this before changing _state, we don't want to move out of the 'Disabled' state
        // PoolGroupStateUninitialized = 0;
        private const int PoolGroupStateActive   = 1; // initial state, GetPoolGroup from cache, connection Open
        private const int PoolGroupStateIdle     = 2; // all pools are pruned via Clear
        private const int PoolGroupStateDisabled = 4; // factory pool entry prunning method

        internal DbConnectionPoolGroup (DbConnectionOptions connectionOptions, DbConnectionPoolKey key, DbConnectionPoolGroupOptions poolGroupOptions) {
            Debug.Assert(null != connectionOptions, "null connection options");
            Debug.Assert(null == poolGroupOptions || ADP.IsWindowsNT, "should not have pooling options on Win9x");

            _connectionOptions = connectionOptions;
            _poolKey = key;
            _poolGroupOptions = poolGroupOptions;

            // always lock this object before changing state
            // HybridDictionary does not create any sub-objects until add
            // so it is safe to use for non-pooled connection as long as
            // we check _poolGroupOptions first
            _poolCollection = new ConcurrentDictionary<DbConnectionPoolIdentity, DbConnectionPool>();
            _state = PoolGroupStateActive; // VSWhidbey 112102
        }

        internal DbConnectionOptions ConnectionOptions {
            get {
                return _connectionOptions;
            }
        }

        internal DbConnectionPoolKey PoolKey {
            get {
                return _poolKey;
            }
        }

        internal DbConnectionPoolGroupProviderInfo ProviderInfo {
            get {
                return _providerInfo;
            }
            set {
                _providerInfo = value;
                if(null!=value) {
                    _providerInfo.PoolGroup = this;
                }
            }
        }

        internal bool IsDisabled {
            get {
                return (PoolGroupStateDisabled == _state);
            }
        }

        internal int ObjectID {
            get {
                return _objectID;
            }
        }

        internal DbConnectionPoolGroupOptions PoolGroupOptions {
            get {
                return _poolGroupOptions;
            }
        }

        internal DbMetaDataFactory MetaDataFactory{
            get {
                return  _metaDataFactory;
                }

            set {
                _metaDataFactory = value;
            }
        }

        internal int Clear() {
            // must be multi-thread safe with competing calls by Clear and Prune via background thread
            // will return the number of connections in the group after clearing has finished

            // First, note the old collection and create a new collection to be used
            ConcurrentDictionary<DbConnectionPoolIdentity, DbConnectionPool> oldPoolCollection = null;
            lock (this) {
                if (_poolCollection.Count > 0) {
                    oldPoolCollection = _poolCollection;
                    _poolCollection = new ConcurrentDictionary<DbConnectionPoolIdentity, DbConnectionPool>();
                }
            }

            // Then, if a new collection was created, release the pools from the old collection
            if (oldPoolCollection != null) {
                foreach (var entry in oldPoolCollection) {
                    DbConnectionPool pool = entry.Value;
                    if (pool != null) {
                        // 







                        DbConnectionFactory connectionFactory = pool.ConnectionFactory;
#if !MOBILE
                        connectionFactory.PerformanceCounters.NumberOfActiveConnectionPools.Decrement();
#endif
                        connectionFactory.QueuePoolForRelease(pool, true);
                    }
                }
            }

            // Finally, return the pool collection count - this may be non-zero if something was added while we were clearing
            return _poolCollection.Count;
        }

        internal DbConnectionPool GetConnectionPool(DbConnectionFactory connectionFactory) {
            // When this method returns null it indicates that the connection
            // factory should not use pooling.

            // We don't support connection pooling on Win9x; it lacks too
            // many of the APIs we require.
            // PoolGroupOptions will only be null when we're not supposed to pool
            // connections.
            DbConnectionPool pool = null;
            if (null != _poolGroupOptions) {
                Debug.Assert(ADP.IsWindowsNT, "should not be pooling on Win9x");

                DbConnectionPoolIdentity currentIdentity = DbConnectionPoolIdentity.NoIdentity;
                if (_poolGroupOptions.PoolByIdentity) {
                    // if we're pooling by identity (because integrated security is
                    // being used for these connections) then we need to go out and
                    // search for the connectionPool that matches the current identity.

                    currentIdentity = DbConnectionPoolIdentity.GetCurrent();

                    // If the current token is restricted in some way, then we must
                    // not attempt to pool these connections.
                    if (currentIdentity.IsRestricted) {
                        currentIdentity = null;
                    }
                }
                if (null != currentIdentity) {
                    if (!_poolCollection.TryGetValue(currentIdentity, out pool)) { // find the pool
                        DbConnectionPoolProviderInfo connectionPoolProviderInfo = connectionFactory.CreateConnectionPoolProviderInfo(this.ConnectionOptions);

                        // optimistically create pool, but its callbacks are delayed until after actual add
                        DbConnectionPool newPool = new DbConnectionPool(connectionFactory, this, currentIdentity, connectionPoolProviderInfo);

                        lock (this) {
                            // Did someone already add it to the list?
                            if (!_poolCollection.TryGetValue(currentIdentity, out pool)) {
                                if (MarkPoolGroupAsActive()) {
                                    // If we get here, we know for certain that we there isn't
                                    // a pool that matches the current identity, so we have to
                                    // add the optimistically created one
                                    newPool.Startup(); // must start pool before usage
                                    bool addResult = _poolCollection.TryAdd(currentIdentity, newPool);
                                    Debug.Assert(addResult, "No other pool with current identity should exist at this point");
#if !MOBILE
                                    connectionFactory.PerformanceCounters.NumberOfActiveConnectionPools.Increment();
#endif
                                    pool = newPool;
                                    newPool = null;
                                }
                                else {
                                    // else pool entry has been disabled so don't create new pools
                                    Debug.Assert(PoolGroupStateDisabled == _state, "state should be disabled");
                                }
                            }
                            else {
                                // else found an existing pool to use instead
                                Debug.Assert(PoolGroupStateActive == _state, "state should be active since a pool exists and lock holds");
                            }
                        }

                        if (null != newPool) {
                            // don't need to call connectionFactory.QueuePoolForRelease(newPool) because
                            // pool callbacks were delayed and no risk of connections being created
                            newPool.Shutdown();
                        }
                    }
                    // the found pool could be in any state
                }
            }

            if (null == pool) {
                lock(this) {
                    // keep the pool entry state active when not pooling
                    MarkPoolGroupAsActive();
                }
            }
            return pool;
        }

        private bool MarkPoolGroupAsActive() {
            // when getting a connection, make the entry active if it was idle (but not disabled)
            // must always lock this before calling

            if (PoolGroupStateIdle == _state) {
                _state = PoolGroupStateActive;
                Bid.Trace("<prov.DbConnectionPoolGroup.ClearInternal|RES|INFO|CPOOL> %d#, Active\n", ObjectID);
            }
            return (PoolGroupStateActive == _state);
        }

        internal bool Prune() {
            // must only call from DbConnectionFactory.PruneConnectionPoolGroups on background timer thread
            // must lock(DbConnectionFactory._connectionPoolGroups.SyncRoot) before calling ReadyToRemove
            //     to avoid conflict with DbConnectionFactory.CreateConnectionPoolGroup replacing pool entry
            lock (this) {
                if (_poolCollection.Count > 0) {
                    var newPoolCollection = new ConcurrentDictionary<DbConnectionPoolIdentity, DbConnectionPool>();

                    foreach (var entry in _poolCollection) {
                        DbConnectionPool pool = entry.Value;
                        if (pool != null) {
                            // 







                            // Actually prune the pool if there are no connections in the pool and no errors occurred.
                            // Empty pool during pruning indicates zero or low activity, but
                            //  an error state indicates the pool needs to stay around to
                            //  throttle new connection attempts.
                            if ((!pool.ErrorOccurred) && (0 == pool.Count)) {

                                // Order is important here.  First we remove the pool
                                // from the collection of pools so no one will try
                                // to use it while we're processing and finally we put the
                                // pool into a list of pools to be released when they
                                // are completely empty.
                                DbConnectionFactory connectionFactory = pool.ConnectionFactory;

#if !MOBILE
                                connectionFactory.PerformanceCounters.NumberOfActiveConnectionPools.Decrement();
#endif
                                connectionFactory.QueuePoolForRelease(pool, false);
                            }
                            else {
                                newPoolCollection.TryAdd(entry.Key, entry.Value);
                            }
                        }
                    }
                    _poolCollection = newPoolCollection;
                }

                // must be pruning thread to change state and no connections
                // otherwise pruning thread risks making entry disabled soon after user calls ClearPool
                if (0 == _poolCollection.Count) {
                    if (PoolGroupStateActive == _state) {
                        _state = PoolGroupStateIdle;
                        Bid.Trace("<prov.DbConnectionPoolGroup.ClearInternal|RES|INFO|CPOOL> %d#, Idle\n", ObjectID);
                    }
                    else if (PoolGroupStateIdle == _state) {
                        _state = PoolGroupStateDisabled;
                        Bid.Trace("<prov.DbConnectionPoolGroup.ReadyToRemove|RES|INFO|CPOOL> %d#, Disabled\n", ObjectID);
                    }
                }
                return (PoolGroupStateDisabled == _state);
            }
        }
    }
}