Index: System.Data.SQLite/SQLite3.cs ================================================================== --- System.Data.SQLite/SQLite3.cs +++ System.Data.SQLite/SQLite3.cs @@ -291,14 +291,17 @@ } internal override int CountPool() { Dictionary counts = null; + int openCount = 0; + int closeCount = 0; int totalCount = 0; SQLiteConnectionPool.GetCounts(_fileName, - ref counts, ref totalCount); + ref counts, ref openCount, ref closeCount, + ref totalCount); return totalCount; } internal override void SetTimeout(int nTimeoutMS) Index: System.Data.SQLite/SQLiteBase.cs ================================================================== --- System.Data.SQLite/SQLiteBase.cs +++ System.Data.SQLite/SQLiteBase.cs @@ -394,10 +394,11 @@ } internal static void ResetConnection(SQLiteConnectionHandle hdl, IntPtr db) { if ((hdl == null) || (db == IntPtr.Zero)) return; + if (hdl.IsClosed || hdl.IsInvalid) return; lock (hdl) { IntPtr stmt = IntPtr.Zero; int n; do @@ -417,10 +418,11 @@ { n = UnsafeNativeMethods.sqlite3_exec(db, ToUTF8("ROLLBACK"), IntPtr.Zero, IntPtr.Zero, out stmt); if (n > 0) throw new SQLiteException(n, GetLastError(hdl, db)); } } + GC.KeepAlive(hdl); } internal static bool IsAutocommit(IntPtr db) { if (db == IntPtr.Zero) return false; Index: System.Data.SQLite/SQLiteConnectionPool.cs ================================================================== --- System.Data.SQLite/SQLiteConnectionPool.cs +++ System.Data.SQLite/SQLiteConnectionPool.cs @@ -6,11 +6,12 @@ ********************************************************/ namespace System.Data.SQLite { using System; - using System.Collections.Generic; + using System.Collections.Generic; + using System.Threading; internal static class SQLiteConnectionPool { /// /// Keeps track of connections made on a specified file. The PoolVersion dictates whether old objects get @@ -35,26 +36,45 @@ private static SortedList _connections = new SortedList(StringComparer.OrdinalIgnoreCase); /// /// The default version number new pools will get /// - private static int _poolVersion = 1; + private static int _poolVersion = 1; + + /// + /// The number of connections successfully opened from any pool. + /// This value is incremented by the Remove method. + /// + private static int _poolOpened = 0; + + /// + /// The number of connections successfully closed from any pool. + /// This value is incremented by the Add method. + /// + private static int _poolClosed = 0; /// /// Counts the number of pool entries matching the specified file name. /// /// The file name to match or null to match all files. - /// The pool entry counts for each matching file. + /// The pool entry counts for each matching file. + /// The total number of connections successfully opened from any pool. + /// The total number of connections successfully closed from any pool. /// The total number of pool entries for all matching files. internal static void GetCounts( string fileName, ref Dictionary counts, + ref int openCount, + ref int closeCount, ref int totalCount ) { lock (_connections) - { + { + openCount = _poolOpened; + closeCount = _poolClosed; + if (counts == null) { counts = new Dictionary( StringComparer.OrdinalIgnoreCase); } @@ -129,11 +149,12 @@ while (poolQueue.Count > 0) { WeakReference cnn = poolQueue.Dequeue(); SQLiteConnectionHandle hdl = cnn.Target as SQLiteConnectionHandle; if ((hdl != null) && !hdl.IsClosed && !hdl.IsInvalid) - { + { + Interlocked.Increment(ref _poolOpened); return hdl; } GC.KeepAlive(hdl); } return null; @@ -147,14 +168,19 @@ internal static void ClearAllPools() { lock (_connections) { foreach (KeyValuePair pair in _connections) - { - while (pair.Value.Queue.Count > 0) - { - WeakReference cnn = pair.Value.Queue.Dequeue(); + { + if (pair.Value == null) + continue; + + Queue poolQueue = pair.Value.Queue; + + while (poolQueue.Count > 0) + { + WeakReference cnn = poolQueue.Dequeue(); SQLiteConnectionHandle hdl = cnn.Target as SQLiteConnectionHandle; if (hdl != null) { hdl.Dispose(); } @@ -224,11 +250,12 @@ ResizePool(queue, true); Queue poolQueue = queue.Queue; if (poolQueue == null) return; - poolQueue.Enqueue(new WeakReference(hdl, false)); + poolQueue.Enqueue(new WeakReference(hdl, false)); + Interlocked.Increment(ref _poolClosed); GC.KeepAlive(hdl); } else { hdl.Close(); Index: Tests/tkt-996d13cd87.eagle ================================================================== --- Tests/tkt-996d13cd87.eagle +++ Tests/tkt-996d13cd87.eagle @@ -54,10 +54,16 @@ // // NOTE: This is the total number of test threads to create. // int count = 100; + // + // NOTE: This is the total number of times we should force a full + // garbage collection. + // + int gcCount = 2; + // // NOTE: Create a random number generator suitable for waiting a // random number of milliseconds between each attempt to // cause the race condition on a given thread. // @@ -131,12 +137,15 @@ // // NOTE: Wait a random number of milliseconds before forcing a // full garbage collection. // - Thread.Sleep(random.Next(0, 1000)); - GC.GetTotalMemory(true); + for (int index = 0; index < gcCount; index++) + { + Thread.Sleep(random.Next(0, 1000)); + GC.GetTotalMemory(true); + } } catch (Exception e) { Interlocked.Increment(ref errors); Trace.WriteLine(e); @@ -210,21 +219,26 @@ list $code $results \ [expr {[info exists errors] ? $errors : ""}] \ [expr {$code eq "Ok" ? [catch { object invoke _Dynamic${id}.Test${id} Main - } result] : [set result ""]}] $result + } result] : [set result ""]}] $result \ + [expr {[object invoke -flags +NonPublic \ + System.Data.SQLite.SQLiteConnectionPool _poolOpened] > 0}] \ + [expr {[object invoke -flags +NonPublic \ + System.Data.SQLite.SQLiteConnectionPool _poolClosed] > 0}] } -cleanup { object invoke System.Data.SQLite.SQLiteConnection ClearAllPools object invoke GC GetTotalMemory true cleanupDb $fileName unset -nocomplain result results errors code sql dataSource id db fileName } -constraints \ {eagle monoBug28 command.sql compile.DATA SQLite System.Data.SQLite} -match \ -regexp -result {^Ok System#CodeDom#Compiler#CompilerResults#\d+ \{\} 0 \d+$}} +regexp -result {^Ok System#CodeDom#Compiler#CompilerResults#\d+ \{\} 0 \d+ True\ +True$}} ############################################################################### runSQLiteTestEpilogue runTestEpilogue