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 (robert@blackcastlesoft.com)
*
* 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();
}
}
}
}
}
|