forked from jstedfast/MimeKit
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathSQLiteConnectionPool.cs
192 lines (173 loc) · 6.61 KB
/
SQLiteConnectionPool.cs
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
/********************************************************
* ADO.NET 2.0 Data Provider for SQLite Version 3.X
* Written by Robert Simpson ([email protected])
*
* Released to the public domain, use at your own risk!
********************************************************/
namespace Mono.Data.Sqlite
{
using System;
using System.Collections.Generic;
internal static class SqliteConnectionPool
{
/// <summary>
/// Keeps track of connections made on a specified file. The PoolVersion dictates whether old objects get
/// returned to the pool or discarded when no longer in use.
/// </summary>
internal class Pool
{
internal readonly Queue<WeakReference> Queue = new Queue<WeakReference>();
internal int PoolVersion;
internal int MaxPoolSize;
internal Pool(int version, int maxSize)
{
PoolVersion = version;
MaxPoolSize = maxSize;
}
}
/// <summary>
/// The connection pool object
/// </summary>
private static SortedList<string, Pool> _connections = new SortedList<string, Pool>(StringComparer.OrdinalIgnoreCase);
/// <summary>
/// The default version number new pools will get
/// </summary>
private static int _poolVersion = 1;
/// <summary>
/// Attempt to pull a pooled connection out of the queue for active duty
/// </summary>
/// <param name="fileName">The filename for a desired connection</param>
/// <param name="maxPoolSize">The maximum size the connection pool for the filename can be</param>
/// <param name="version">The pool version the returned connection will belong to</param>
/// <returns>Returns NULL if no connections were available. Even if none are, the poolversion will still be a valid pool version</returns>
internal static SqliteConnectionHandle Remove(string fileName, int maxPoolSize, out int version)
{
lock (_connections)
{
Pool queue;
// Default to the highest pool version
version = _poolVersion;
// If we didn't find a pool for this file, create one even though it will be empty.
// We have to do this here because otherwise calling ClearPool() on the file will not work for active connections
// that have never seen the pool yet.
if (_connections.TryGetValue(fileName, out queue) == false)
{
queue = new Pool(_poolVersion, maxPoolSize);
_connections.Add(fileName, queue);
return null;
}
// We found a pool for this file, so use its version number
version = queue.PoolVersion;
queue.MaxPoolSize = maxPoolSize;
ResizePool(queue, false);
// Try and get a pooled connection from the queue
while (queue.Queue.Count > 0)
{
WeakReference cnn = queue.Queue.Dequeue();
SqliteConnectionHandle hdl = cnn.Target as SqliteConnectionHandle;
if (hdl != null)
{
return hdl;
}
}
return null;
}
}
/// <summary>
/// Clears out all pooled connections and rev's up the default pool version to force all old active objects
/// not in the pool to get discarded rather than returned to their pools.
/// </summary>
internal static void ClearAllPools()
{
lock (_connections)
{
foreach (KeyValuePair<string, Pool> pair in _connections)
{
while (pair.Value.Queue.Count > 0)
{
WeakReference cnn = pair.Value.Queue.Dequeue();
SqliteConnectionHandle hdl = cnn.Target as SqliteConnectionHandle;
if (hdl != null)
{
hdl.Dispose();
}
}
// Keep track of the highest revision so we can go one higher when we're finished
if (_poolVersion <= pair.Value.PoolVersion)
_poolVersion = pair.Value.PoolVersion + 1;
}
// All pools are cleared and we have a new highest version number to force all old version active items to get discarded
// instead of going back to the queue when they are closed.
// We can get away with this because we're pumped up the _poolVersion out of range of all active connections, so they
// will all get discarded when they try to put themselves back in their pool.
_connections.Clear();
}
}
/// <summary>
/// Clear a given pool for a given filename. Discards anything in the pool for the given file, and revs the pool
/// version so current active objects on the old version of the pool will get discarded rather than be returned to the pool.
/// </summary>
/// <param name="fileName">The filename of the pool to clear</param>
internal static void ClearPool(string fileName)
{
lock (_connections)
{
Pool queue;
if (_connections.TryGetValue(fileName, out queue) == true)
{
queue.PoolVersion++;
while (queue.Queue.Count > 0)
{
WeakReference cnn = queue.Queue.Dequeue();
SqliteConnectionHandle hdl = cnn.Target as SqliteConnectionHandle;
if (hdl != null)
{
hdl.Dispose();
}
}
}
}
}
/// <summary>
/// Return a connection to the pool for someone else to use.
/// </summary>
/// <param name="fileName">The filename of the pool to use</param>
/// <param name="hdl">The connection handle to pool</param>
/// <param name="version">The pool version the handle was created under</param>
/// <remarks>
/// If the version numbers don't match between the connection and the pool, then the handle is discarded.
/// </remarks>
internal static void Add(string fileName, SqliteConnectionHandle hdl, int version)
{
lock (_connections)
{
// If the queue doesn't exist in the pool, then it must've been cleared sometime after the connection was created.
Pool queue;
if (_connections.TryGetValue(fileName, out queue) == true && version == queue.PoolVersion)
{
ResizePool(queue, true);
queue.Queue.Enqueue(new WeakReference(hdl, false));
GC.KeepAlive(hdl);
}
else
{
hdl.Close();
}
}
}
private static void ResizePool(Pool queue, bool forAdding)
{
int target = queue.MaxPoolSize;
if (forAdding && target > 0) target--;
while (queue.Queue.Count > target)
{
WeakReference cnn = queue.Queue.Dequeue();
SqliteConnectionHandle hdl = cnn.Target as SqliteConnectionHandle;
if (hdl != null)
{
hdl.Dispose();
}
}
}
}
}