System.Data.SQLite
Check-in [90142c95cc]
Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Merge custom connection pool support to trunk. Pursuant to [393d954be0].
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 90142c95cc143741c5b415cfe98d410bd393ccae
User & Date: mistachkin 2013-02-18 21:31:06
References
2013-02-19
09:08 Ticket [393d954be0] Deadlock in default connection pool implementation - support custom connection pool status still Closed with 3 other changes artifact: 159b815777 user: mistachkin
Context
2013-02-19
09:55
Add BindAllAsText connection flag to force binding of all values as text. check-in: cb62e486b5 user: mistachkin tags: trunk
2013-02-18
21:31
Merge custom connection pool support to trunk. Pursuant to [393d954be0]. check-in: 90142c95cc user: mistachkin tags: trunk
2013-02-16
02:17
Add testing for the custom connection pool functionality. Closed-Leaf check-in: 8549eb3248 user: mistachkin tags: customPool
00:09
Update SQLite core library to the latest trunk. check-in: cf6d124b7d user: mistachkin tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to Doc/Extra/version.html.

    45     45       <h1 class="heading">Version History</h1>
    46     46       <p><b>1.0.85.0 - March XX, 2013 <font color="red">(release scheduled)</font></b></p>
    47     47       <ul>
    48     48         <li>Updated to <a href="http://www.sqlite.org/src/info/trunk">SQLite 3.7.16</a>.</li>
    49     49         <li>Skip checking loaded assemblies for types tagged with the SQLiteFunction attribute when the No_SQLiteFunctions environment variable is set. Pursuant to <a href="http://system.data.sqlite.org/index.html/info/e4c8121f7b">[e4c8121f7b]</a>.</li>
    50     50         <li>Add HexPassword connection string property to work around the inability to include a literal semicolon in a connection string property value. Pursuant to <a href="http://system.data.sqlite.org/index.html/info/1c456ae75f">[1c456ae75f]</a>.</li>
    51     51         <li>Add static Execute method to the SQLiteCommand class.</li>
           52  +      <li>Support custom connection pool implementations by adding the ISQLiteConnectionPool interface, the static SQLiteConnection.ConnectionPool property, and the static CreateHandle method in addition to modifying the SQLiteConnectionPool class. Pursuant to <a href="http://system.data.sqlite.org/index.html/info/393d954be0">[393d954be0]</a>.</li>
    52     53         <li>Add public constructor to the SQLiteDataAdapter class that allows passing the parseViaFramework parameter to the SQLiteConnection constructor.</li>
    53     54         <li>When built with the CHECK_STATE compile-time option, skip throwing exceptions from the SQLiteDataReader class when the object is being disposed.</li>
    54     55         <li>Support automatic value conversions for columns with a declared type of BIGUINT, INTEGER8, INTEGER16, INTEGER32, INTEGER64, SMALLUINT, TINYSINT, UNSIGNEDINTEGER, UNSIGNEDINTEGER8, UNSIGNEDINTEGER16, UNSIGNEDINTEGER32, UNSIGNEDINTEGER64, INT8, INT16, INT32, INT64, UINT, UINT8, UINT16, UINT32, UINT64, or ULONG.</li>
    55     56         <li>Add BindUInt32AsInt64 connection flag to force binding of UInt32 values as Int64 instead. Pursuant to <a href="http://system.data.sqlite.org/index.html/info/c010fa6584">[c010fa6584]</a>.</li>
    56     57         <li>Remove AUTOINCREMENT from the column type name map.&nbsp;<b>** Potentially Incompatible Change **</b></li>
    57     58         <li>Avoid throwing overflow exceptions from the SQLite3.GetValue method for integral column types. Partial fix for <a href="http://system.data.sqlite.org/index.html/info/c010fa6584">[c010fa6584]</a>.&nbsp;<b>** Potentially Incompatible Change **</b></li>
    58     59         <li>Use the legacy connection closing algorithm when built with the INTEROP_LEGACY_CLOSE compile-time option.</li>

Changes to System.Data.SQLite/SQLiteConnection.cs.

   195    195     /// <description>Y</description>
   196    196     /// </item>
   197    197     /// <item>
   198    198     /// <description>Pooling</description>
   199    199     /// <description>
   200    200     /// <b>True</b> - Use connection pooling.<br/>
   201    201     /// <b>False</b> - Do not use connection pooling.<br/><br/>
   202         -  /// <b>WARNING:</b> Setting this property to True should be avoided by
   203         -  /// applications that make use of WPF (either directly or indirectly) due
   204         -  /// to possible deadlocks that can occur during the finalization of some
   205         -  /// WPF objects.
          202  +  /// <b>WARNING:</b> When using the default connection pool implementation,
          203  +  /// setting this property to True should be avoided by applications that make
          204  +  /// use of COM (either directly or indirectly) due to possible deadlocks that
          205  +  /// can occur during the finalization of some COM objects.
   206    206     /// </description>
   207    207     /// <description>N</description>
   208    208     /// <description>False</description>
   209    209     /// </item>
   210    210     /// <item>
   211    211     /// <description>FailIfMissing</description>
   212    212     /// <description><b>True</b> - Don't create the database if it does not exist, throw an error instead<br/><b>False</b> - Automatically create the database if it does not exist</description>
................................................................................
   634    634           {
   635    635               lock (_syncRoot)
   636    636               {
   637    637                   _handlers -= value;
   638    638               }
   639    639           }
   640    640       }
          641  +
          642  +    ///////////////////////////////////////////////////////////////////////////////////////////////
          643  +
          644  +    /// <summary>
          645  +    /// This property is used to obtain or set the custom connection pool
          646  +    /// implementation to use, if any.  Setting this property to null will
          647  +    /// cause the default connection pool implementation to be used.
          648  +    /// </summary>
          649  +    public static ISQLiteConnectionPool ConnectionPool
          650  +    {
          651  +        get { return SQLiteConnectionPool.GetConnectionPool(); }
          652  +        set { SQLiteConnectionPool.SetConnectionPool(value); }
          653  +    }
          654  +
          655  +    ///////////////////////////////////////////////////////////////////////////////////////////////
          656  +
          657  +    /// <summary>
          658  +    /// Creates and returns a new managed database connection handle.  This
          659  +    /// method is intended to be used by implementations of the
          660  +    /// <see cref="ISQLiteConnectionPool" /> interface only.  In theory, it
          661  +    /// could be used by other classes; however, that usage is not supported.
          662  +    /// </summary>
          663  +    /// <param name="nativeHandle">
          664  +    /// This must be a native database connection handle returned by the
          665  +    /// SQLite core library and it must remain valid and open during the
          666  +    /// entire duration of the calling method.
          667  +    /// </param>
          668  +    /// <returns>
          669  +    /// The new managed database connection handle or null if it cannot be
          670  +    /// created.
          671  +    /// </returns>
          672  +    public static object CreateHandle(
          673  +        IntPtr nativeHandle
          674  +        )
          675  +    {
          676  +        if (nativeHandle == IntPtr.Zero) return null;
          677  +        return new SQLiteConnectionHandle(nativeHandle);
          678  +    }
   641    679   
   642    680       ///////////////////////////////////////////////////////////////////////////////////////////////
   643    681   
   644    682       #region Backup API Members
   645    683       /// <summary>
   646    684       /// Backs up the database, using the specified database connection as the
   647    685       /// destination.
................................................................................
  1173   1211       /// <description>Y</description>
  1174   1212       /// </item>
  1175   1213       /// <item>
  1176   1214       /// <description>Pooling</description>
  1177   1215       /// <description>
  1178   1216       /// <b>True</b> - Use connection pooling.<br/>
  1179   1217       /// <b>False</b> - Do not use connection pooling.<br/><br/>
  1180         -    /// <b>WARNING:</b> Setting this property to True should be avoided by
  1181         -    /// applications that make use of WPF (either directly or indirectly) due
  1182         -    /// to possible deadlocks that can occur during the finalization of some
  1183         -    /// WPF objects.
         1218  +    /// <b>WARNING:</b> When using the default connection pool implementation,
         1219  +    /// setting this property to True should be avoided by applications that
         1220  +    /// make use of COM (either directly or indirectly) due to possible
         1221  +    /// deadlocks that can occur during the finalization of some COM objects.
  1184   1222       /// </description>
  1185   1223       /// <description>N</description>
  1186   1224       /// <description>False</description>
  1187   1225       /// </item>
  1188   1226       /// <item>
  1189   1227       /// <description>FailIfMissing</description>
  1190   1228       /// <description><b>True</b> - Don't create the database if it does not exist, throw an error instead<br/><b>False</b> - Automatically create the database if it does not exist</description>

Changes to System.Data.SQLite/SQLiteConnectionPool.cs.

     3      3    * Written by Robert Simpson (robert@blackcastlesoft.com)
     4      4    * 
     5      5    * Released to the public domain, use at your own risk!
     6      6    ********************************************************/
     7      7   
     8      8   namespace System.Data.SQLite
     9      9   {
    10         -  using System;
    11         -  using System.Collections.Generic;
    12         -  using System.Threading;
           10  +    using System;
           11  +    using System.Collections.Generic;
    13     12   
    14         -  /// <summary>
    15         -  /// This class should not be used by applications that make use of WPF
    16         -  /// (either directly or indirectly) due to possible deadlocks that can occur
    17         -  /// during finalization of some WPF objects.
    18         -  /// </summary>
    19         -  internal static class SQLiteConnectionPool
    20         -  {
           13  +#if !PLATFORM_COMPACTFRAMEWORK && DEBUG
           14  +    using System.Text;
           15  +#endif
           16  +
           17  +    using System.Threading;
           18  +
           19  +    ///////////////////////////////////////////////////////////////////////////
           20  +
           21  +    #region Null Connection Pool Class
           22  +#if !PLATFORM_COMPACTFRAMEWORK && DEBUG
    21     23       /// <summary>
    22         -    /// Keeps track of connections made on a specified file.  The PoolVersion dictates whether old objects get
    23         -    /// returned to the pool or discarded when no longer in use.
           24  +    /// This class implements a connection pool where all methods of the
           25  +    /// <see cref="ISQLiteConnectionPool" /> interface are NOPs.  This class
           26  +    /// is used for testing purposes only.
    24     27       /// </summary>
    25         -    internal class Pool
           28  +    internal sealed class NullConnectionPool : ISQLiteConnectionPool
    26     29       {
    27         -      internal readonly Queue<WeakReference> Queue = new Queue<WeakReference>();
    28         -      internal int PoolVersion;
    29         -      internal int MaxPoolSize;
    30         -
    31         -      internal Pool(int version, int maxSize)
    32         -      {
    33         -        PoolVersion = version;
    34         -        MaxPoolSize = maxSize;
    35         -      }
           30  +        #region Private Data
           31  +        /// <summary>
           32  +        /// This field keeps track of all method calls made into the
           33  +        /// <see cref="ISQLiteConnectionPool" /> interface methods of this
           34  +        /// class.
           35  +        /// </summary>
           36  +        private StringBuilder log;
           37  +
           38  +        ///////////////////////////////////////////////////////////////////////
           39  +
           40  +        /// <summary>
           41  +        /// Non-zero to dispose of database connection handles received via the
           42  +        /// <see cref="Add" /> method.
           43  +        /// </summary>
           44  +        private bool dispose;
           45  +        #endregion
           46  +
           47  +        ///////////////////////////////////////////////////////////////////////
           48  +
           49  +        #region Private Constructors
           50  +        /// <summary>
           51  +        /// Constructs a connection pool object where all methods of the
           52  +        /// <see cref="ISQLiteConnectionPool" /> interface are NOPs.  This
           53  +        /// class is used for testing purposes only.
           54  +        /// </summary>
           55  +        private NullConnectionPool()
           56  +        {
           57  +            log = new StringBuilder();
           58  +        }
           59  +        #endregion
           60  +
           61  +        ///////////////////////////////////////////////////////////////////////
           62  +
           63  +        #region Public Constructors
           64  +        /// <summary>
           65  +        /// Constructs a connection pool object where all methods of the
           66  +        /// <see cref="ISQLiteConnectionPool" /> interface are NOPs.  This
           67  +        /// class is used for testing purposes only.
           68  +        /// </summary>
           69  +        /// <param name="dispose">
           70  +        /// Non-zero to dispose of database connection handles received via the
           71  +        /// <see cref="Add" /> method.
           72  +        /// </param>
           73  +        public NullConnectionPool(
           74  +            bool dispose
           75  +            )
           76  +            : this()
           77  +        {
           78  +            this.dispose = dispose;
           79  +        }
           80  +        #endregion
           81  +
           82  +        ///////////////////////////////////////////////////////////////////////
           83  +
           84  +        #region ISQLiteConnectionPool Members
           85  +        /// <summary>
           86  +        /// Counts the number of pool entries matching the specified file name.
           87  +        /// </summary>
           88  +        /// <param name="fileName">
           89  +        /// The file name to match or null to match all files.
           90  +        /// </param>
           91  +        /// <param name="counts">
           92  +        /// The pool entry counts for each matching file.
           93  +        /// </param>
           94  +        /// <param name="openCount">
           95  +        /// The total number of connections successfully opened from any pool.
           96  +        /// </param>
           97  +        /// <param name="closeCount">
           98  +        /// The total number of connections successfully closed from any pool.
           99  +        /// </param>
          100  +        /// <param name="totalCount">
          101  +        /// The total number of pool entries for all matching files.
          102  +        /// </param>
          103  +        public void GetCounts(
          104  +            string fileName,
          105  +            ref Dictionary<string, int> counts,
          106  +            ref int openCount,
          107  +            ref int closeCount,
          108  +            ref int totalCount
          109  +            )
          110  +        {
          111  +            if (log != null)
          112  +            {
          113  +                log.AppendFormat(
          114  +                    "GetCounts(\"{0}\", {1}, {2}, {3}, {4}){5}", fileName,
          115  +                    counts, openCount, closeCount, totalCount,
          116  +                    Environment.NewLine);
          117  +            }
          118  +        }
          119  +
          120  +        ///////////////////////////////////////////////////////////////////////
          121  +
          122  +        /// <summary>
          123  +        /// Disposes of all pooled connections associated with the specified
          124  +        /// database file name.
          125  +        /// </summary>
          126  +        /// <param name="fileName">
          127  +        /// The database file name.
          128  +        /// </param>
          129  +        public void ClearPool(
          130  +            string fileName
          131  +            )
          132  +        {
          133  +            if (log != null)
          134  +            {
          135  +                log.AppendFormat(
          136  +                    "ClearPool(\"{0}\"){1}", fileName, Environment.NewLine);
          137  +            }
          138  +        }
          139  +
          140  +        ///////////////////////////////////////////////////////////////////////
          141  +
          142  +        /// <summary>
          143  +        /// Disposes of all pooled connections.
          144  +        /// </summary>
          145  +        public void ClearAllPools()
          146  +        {
          147  +            if (log != null)
          148  +            {
          149  +                log.AppendFormat(
          150  +                    "ClearAllPools(){0}", Environment.NewLine);
          151  +            }
          152  +        }
          153  +
          154  +        ///////////////////////////////////////////////////////////////////////
          155  +
          156  +        /// <summary>
          157  +        /// Adds a connection to the pool of those associated with the
          158  +        /// specified database file name.
          159  +        /// </summary>
          160  +        /// <param name="fileName">
          161  +        /// The database file name.
          162  +        /// </param>
          163  +        /// <param name="handle">
          164  +        /// The database connection handle.
          165  +        /// </param>
          166  +        /// <param name="version">
          167  +        /// The connection pool version at the point the database connection
          168  +        /// handle was received from the connection pool.  This is also the
          169  +        /// connection pool version that the database connection handle was
          170  +        /// created under.
          171  +        /// </param>
          172  +        public void Add(
          173  +            string fileName,
          174  +            object handle,
          175  +            int version
          176  +            )
          177  +        {
          178  +            if (log != null)
          179  +            {
          180  +                log.AppendFormat(
          181  +                    "Add(\"{0}\", {1}, {2}){3}", fileName, handle, version,
          182  +                    Environment.NewLine);
          183  +            }
          184  +
          185  +            //
          186  +            // NOTE: If configured to do so, dispose of the received connection
          187  +            //       handle now.
          188  +            //
          189  +            if (dispose)
          190  +            {
          191  +                IDisposable disposable = handle as IDisposable;
          192  +
          193  +                if (disposable != null)
          194  +                    disposable.Dispose();
          195  +            }
          196  +        }
          197  +
          198  +        ///////////////////////////////////////////////////////////////////////
          199  +
          200  +        /// <summary>
          201  +        /// Removes a connection from the pool of those associated with the
          202  +        /// specified database file name with the intent of using it to
          203  +        /// interact with the database.
          204  +        /// </summary>
          205  +        /// <param name="fileName">
          206  +        /// The database file name.
          207  +        /// </param>
          208  +        /// <param name="maxPoolSize">
          209  +        /// The new maximum size of the connection pool for the specified
          210  +        /// database file name.
          211  +        /// </param>
          212  +        /// <param name="version">
          213  +        /// The connection pool version associated with the returned database
          214  +        /// connection handle, if any.
          215  +        /// </param>
          216  +        /// <returns>
          217  +        /// The database connection handle associated with the specified
          218  +        /// database file name or null if it cannot be obtained.
          219  +        /// </returns>
          220  +        public object Remove(
          221  +            string fileName,
          222  +            int maxPoolSize,
          223  +            out int version
          224  +            )
          225  +        {
          226  +            version = 0;
          227  +
          228  +            if (log != null)
          229  +            {
          230  +                log.AppendFormat(
          231  +                    "Remove(\"{0}\", {1}, {2}){3}", fileName, maxPoolSize,
          232  +                    version, Environment.NewLine);
          233  +            }
          234  +
          235  +            return null;
          236  +        }
          237  +        #endregion
          238  +
          239  +        ///////////////////////////////////////////////////////////////////////
          240  +
          241  +        #region System.Object Overrides
          242  +        /// <summary>
          243  +        /// Overrides the default <see cref="System.Object.ToString" /> method
          244  +        /// to provide a log of all methods called on the
          245  +        /// <see cref="ISQLiteConnectionPool" /> interface.
          246  +        /// </summary>
          247  +        /// <returns>
          248  +        /// A string containing a log of all method calls into the
          249  +        /// <see cref="ISQLiteConnectionPool" /> interface, along with their
          250  +        /// parameters, delimited by <see cref="Environment.NewLine" />.
          251  +        /// </returns>
          252  +        public override string ToString()
          253  +        {
          254  +            return (log != null) ? log.ToString() : String.Empty;
          255  +        }
          256  +        #endregion
    36    257       }
          258  +#endif
          259  +    #endregion
    37    260   
          261  +    ///////////////////////////////////////////////////////////////////////////
          262  +
          263  +    #region Public Connection Pool Interface
    38    264       /// <summary>
    39         -    /// The connection pool object
    40         -    /// </summary>
    41         -    private static SortedList<string, Pool> _connections = new SortedList<string, Pool>(StringComparer.OrdinalIgnoreCase);
    42         -
    43         -    /// <summary>
    44         -    /// The default version number new pools will get
          265  +    /// This interface represents a custom connection pool implementation
          266  +    /// usable by System.Data.SQLite.
    45    267       /// </summary>
    46         -    private static int _poolVersion = 1;
    47         -
    48         -    /// <summary>
    49         -    /// The number of connections successfully opened from any pool.
    50         -    /// This value is incremented by the Remove method.
    51         -    /// </summary>
    52         -    private static int _poolOpened = 0;
    53         -
          268  +    public interface ISQLiteConnectionPool
          269  +    {
          270  +        /// <summary>
          271  +        /// Counts the number of pool entries matching the specified file name.
          272  +        /// </summary>
          273  +        /// <param name="fileName">
          274  +        /// The file name to match or null to match all files.
          275  +        /// </param>
          276  +        /// <param name="counts">
          277  +        /// The pool entry counts for each matching file.
          278  +        /// </param>
          279  +        /// <param name="openCount">
          280  +        /// The total number of connections successfully opened from any pool.
          281  +        /// </param>
          282  +        /// <param name="closeCount">
          283  +        /// The total number of connections successfully closed from any pool.
          284  +        /// </param>
          285  +        /// <param name="totalCount">
          286  +        /// The total number of pool entries for all matching files.
          287  +        /// </param>
          288  +        void GetCounts(string fileName, ref Dictionary<string, int> counts,
          289  +            ref int openCount, ref int closeCount, ref int totalCount);
          290  +
          291  +        /// <summary>
          292  +        /// Disposes of all pooled connections associated with the specified
          293  +        /// database file name.
          294  +        /// </summary>
          295  +        /// <param name="fileName">
          296  +        /// The database file name.
          297  +        /// </param>
          298  +        void ClearPool(string fileName);
          299  +
          300  +        /// <summary>
          301  +        /// Disposes of all pooled connections.
          302  +        /// </summary>
          303  +        void ClearAllPools();
          304  +
          305  +        /// <summary>
          306  +        /// Adds a connection to the pool of those associated with the
          307  +        /// specified database file name.
          308  +        /// </summary>
          309  +        /// <param name="fileName">
          310  +        /// The database file name.
          311  +        /// </param>
          312  +        /// <param name="handle">
          313  +        /// The database connection handle.
          314  +        /// </param>
          315  +        /// <param name="version">
          316  +        /// The connection pool version at the point the database connection
          317  +        /// handle was received from the connection pool.  This is also the
          318  +        /// connection pool version that the database connection handle was
          319  +        /// created under.
          320  +        /// </param>
          321  +        void Add(string fileName, object handle, int version);
          322  +
          323  +        /// <summary>
          324  +        /// Removes a connection from the pool of those associated with the
          325  +        /// specified database file name with the intent of using it to
          326  +        /// interact with the database.
          327  +        /// </summary>
          328  +        /// <param name="fileName">
          329  +        /// The database file name.
          330  +        /// </param>
          331  +        /// <param name="maxPoolSize">
          332  +        /// The new maximum size of the connection pool for the specified
          333  +        /// database file name.
          334  +        /// </param>
          335  +        /// <param name="version">
          336  +        /// The connection pool version associated with the returned database
          337  +        /// connection handle, if any.
          338  +        /// </param>
          339  +        /// <returns>
          340  +        /// The database connection handle associated with the specified
          341  +        /// database file name or null if it cannot be obtained.
          342  +        /// </returns>
          343  +        object Remove(string fileName, int maxPoolSize, out int version);
          344  +    }
          345  +    #endregion
          346  +
          347  +    ///////////////////////////////////////////////////////////////////////////
          348  +
          349  +    #region Connection Pool Subsystem & Default Implementation
    54    350       /// <summary>
    55         -    /// The number of connections successfully closed from any pool.
    56         -    /// This value is incremented by the Add method.
    57         -    /// </summary>
    58         -    private static int _poolClosed = 0;
    59         -
    60         -    /// <summary>
    61         -    /// Counts the number of pool entries matching the specified file name.
          351  +    /// This default method implementations in this class should not be used by
          352  +    /// applications that make use of COM (either directly or indirectly) due
          353  +    /// to possible deadlocks that can occur during finalization of some COM
          354  +    /// objects.
    62    355       /// </summary>
    63         -    /// <param name="fileName">The file name to match or null to match all files.</param>
    64         -    /// <param name="counts">The pool entry counts for each matching file.</param>
    65         -    /// <param name="openCount">The total number of connections successfully opened from any pool.</param>
    66         -    /// <param name="closeCount">The total number of connections successfully closed from any pool.</param>
    67         -    /// <param name="totalCount">The total number of pool entries for all matching files.</param>
    68         -    internal static void GetCounts(
    69         -        string fileName,
    70         -        ref Dictionary<string, int> counts,
    71         -        ref int openCount,
    72         -        ref int closeCount,
    73         -        ref int totalCount
    74         -        )
          356  +    internal static class SQLiteConnectionPool
    75    357       {
    76         -        lock (_connections)
          358  +        #region Private Pool Class
          359  +        /// <summary>
          360  +        /// Keeps track of connections made on a specified file.  The PoolVersion
          361  +        /// dictates whether old objects get returned to the pool or discarded
          362  +        /// when no longer in use.
          363  +        /// </summary>
          364  +        private sealed class PoolQueue
          365  +        {
          366  +            #region Private Data
          367  +            /// <summary>
          368  +            /// The queue of weak references to the actual database connection
          369  +            /// handles.
          370  +            /// </summary>
          371  +            internal readonly Queue<WeakReference> Queue =
          372  +                new Queue<WeakReference>();
          373  +
          374  +            ///////////////////////////////////////////////////////////////////
          375  +
          376  +            /// <summary>
          377  +            /// This pool version associated with the database connection
          378  +            /// handles in this pool queue.
          379  +            /// </summary>
          380  +            internal int PoolVersion;
          381  +
          382  +            ///////////////////////////////////////////////////////////////////
          383  +
          384  +            /// <summary>
          385  +            /// The maximum size of this pool queue.
          386  +            /// </summary>
          387  +            internal int MaxPoolSize;
          388  +            #endregion
          389  +
          390  +            ///////////////////////////////////////////////////////////////////
          391  +
          392  +            #region Private Constructors
          393  +            /// <summary>
          394  +            /// Constructs a connection pool queue using the specified version
          395  +            /// and maximum size.  Normally, all the database connection
          396  +            /// handles in this pool are associated with a single database file
          397  +            /// name.
          398  +            /// </summary>
          399  +            /// <param name="version">
          400  +            /// The initial pool version for this connection pool queue.
          401  +            /// </param>
          402  +            /// <param name="maxSize">
          403  +            /// The initial maximum size for this connection pool queue.
          404  +            /// </param>
          405  +            internal PoolQueue(
          406  +                int version,
          407  +                int maxSize
          408  +                )
          409  +            {
          410  +                PoolVersion = version;
          411  +                MaxPoolSize = maxSize;
          412  +            }
          413  +            #endregion
          414  +        }
          415  +        #endregion
          416  +
          417  +        ///////////////////////////////////////////////////////////////////////
          418  +
          419  +        #region Private Static Data
          420  +        /// <summary>
          421  +        /// This field is used to synchronize access to the private static data
          422  +        /// in this class.
          423  +        /// </summary>
          424  +        private static readonly object _syncRoot = new object();
          425  +
          426  +        ///////////////////////////////////////////////////////////////////////
          427  +
          428  +        /// <summary>
          429  +        /// When this field is non-null, it will be used to provide the
          430  +        /// implementation of all the connection pool methods; otherwise,
          431  +        /// the default method implementations will be used.
          432  +        /// </summary>
          433  +        private static ISQLiteConnectionPool _connectionPool = null;
          434  +
          435  +        ///////////////////////////////////////////////////////////////////////
          436  +
          437  +        /// <summary>
          438  +        /// The dictionary of connection pools, based on the normalized file
          439  +        /// name of the SQLite database.
          440  +        /// </summary>
          441  +        private static SortedList<string, PoolQueue> _queueList =
          442  +            new SortedList<string, PoolQueue>(StringComparer.OrdinalIgnoreCase);
          443  +
          444  +        ///////////////////////////////////////////////////////////////////////
          445  +
          446  +        /// <summary>
          447  +        /// The default version number new pools will get.
          448  +        /// </summary>
          449  +        private static int _poolVersion = 1;
          450  +
          451  +        ///////////////////////////////////////////////////////////////////////
          452  +
          453  +        /// <summary>
          454  +        /// The number of connections successfully opened from any pool.
          455  +        /// This value is incremented by the Remove method.
          456  +        /// </summary>
          457  +        private static int _poolOpened = 0;
          458  +
          459  +        ///////////////////////////////////////////////////////////////////////
          460  +
          461  +        /// <summary>
          462  +        /// The number of connections successfully closed from any pool.
          463  +        /// This value is incremented by the Add method.
          464  +        /// </summary>
          465  +        private static int _poolClosed = 0;
          466  +        #endregion
          467  +
          468  +        ///////////////////////////////////////////////////////////////////////
          469  +
          470  +        #region ISQLiteConnectionPool Members (Static, Non-Formal)
          471  +        /// <summary>
          472  +        /// Counts the number of pool entries matching the specified file name.
          473  +        /// </summary>
          474  +        /// <param name="fileName">
          475  +        /// The file name to match or null to match all files.
          476  +        /// </param>
          477  +        /// <param name="counts">
          478  +        /// The pool entry counts for each matching file.
          479  +        /// </param>
          480  +        /// <param name="openCount">
          481  +        /// The total number of connections successfully opened from any pool.
          482  +        /// </param>
          483  +        /// <param name="closeCount">
          484  +        /// The total number of connections successfully closed from any pool.
          485  +        /// </param>
          486  +        /// <param name="totalCount">
          487  +        /// The total number of pool entries for all matching files.
          488  +        /// </param>
          489  +        internal static void GetCounts(
          490  +            string fileName,
          491  +            ref Dictionary<string, int> counts,
          492  +            ref int openCount,
          493  +            ref int closeCount,
          494  +            ref int totalCount
          495  +            )
          496  +        {
          497  +            ISQLiteConnectionPool connectionPool = GetConnectionPool();
          498  +
          499  +            if (connectionPool != null)
          500  +            {
          501  +                connectionPool.GetCounts(
          502  +                    fileName, ref counts, ref openCount, ref closeCount,
          503  +                    ref totalCount);
          504  +            }
          505  +            else
          506  +            {
          507  +                lock (_syncRoot)
          508  +                {
          509  +                    openCount = _poolOpened;
          510  +                    closeCount = _poolClosed;
          511  +
          512  +                    if (counts == null)
          513  +                    {
          514  +                        counts = new Dictionary<string, int>(
          515  +                            StringComparer.OrdinalIgnoreCase);
          516  +                    }
          517  +
          518  +                    if (fileName != null)
          519  +                    {
          520  +                        PoolQueue queue;
          521  +
          522  +                        if (_queueList.TryGetValue(fileName, out queue))
          523  +                        {
          524  +                            Queue<WeakReference> poolQueue = queue.Queue;
          525  +                            int count = (poolQueue != null) ? poolQueue.Count : 0;
          526  +
          527  +                            counts.Add(fileName, count);
          528  +                            totalCount += count;
          529  +                        }
          530  +                    }
          531  +                    else
          532  +                    {
          533  +                        foreach (KeyValuePair<string, PoolQueue> pair in _queueList)
          534  +                        {
          535  +                            if (pair.Value == null)
          536  +                                continue;
          537  +
          538  +                            Queue<WeakReference> poolQueue = pair.Value.Queue;
          539  +                            int count = (poolQueue != null) ? poolQueue.Count : 0;
          540  +
          541  +                            counts.Add(pair.Key, count);
          542  +                            totalCount += count;
          543  +                        }
          544  +                    }
          545  +                }
          546  +            }
          547  +        }
          548  +
          549  +        ///////////////////////////////////////////////////////////////////////
          550  +
          551  +        /// <summary>
          552  +        /// Disposes of all pooled connections associated with the specified
          553  +        /// database file name.
          554  +        /// </summary>
          555  +        /// <param name="fileName">
          556  +        /// The database file name.
          557  +        /// </param>
          558  +        internal static void ClearPool(string fileName)
          559  +        {
          560  +            ISQLiteConnectionPool connectionPool = GetConnectionPool();
          561  +
          562  +            if (connectionPool != null)
          563  +            {
          564  +                connectionPool.ClearPool(fileName);
          565  +            }
          566  +            else
          567  +            {
          568  +                lock (_syncRoot)
          569  +                {
          570  +                    PoolQueue queue;
          571  +
          572  +                    if (_queueList.TryGetValue(fileName, out queue))
          573  +                    {
          574  +                        queue.PoolVersion++;
          575  +
          576  +                        Queue<WeakReference> poolQueue = queue.Queue;
          577  +                        if (poolQueue == null) return;
          578  +
          579  +                        while (poolQueue.Count > 0)
          580  +                        {
          581  +                            WeakReference connection = poolQueue.Dequeue();
          582  +
          583  +                            if (connection == null) continue;
          584  +
          585  +                            SQLiteConnectionHandle handle =
          586  +                                connection.Target as SQLiteConnectionHandle;
          587  +
          588  +                            if (handle != null)
          589  +                                handle.Dispose();
          590  +
          591  +                            GC.KeepAlive(handle);
          592  +                        }
          593  +                    }
          594  +                }
          595  +            }
          596  +        }
          597  +
          598  +        ///////////////////////////////////////////////////////////////////////
          599  +
          600  +        /// <summary>
          601  +        /// Disposes of all pooled connections.
          602  +        /// </summary>
          603  +        internal static void ClearAllPools()
    77    604           {
    78         -            openCount = _poolOpened;
    79         -            closeCount = _poolClosed;
          605  +            ISQLiteConnectionPool connectionPool = GetConnectionPool();
    80    606   
    81         -            if (counts == null)
          607  +            if (connectionPool != null)
    82    608               {
    83         -                counts = new Dictionary<string, int>(
    84         -                    StringComparer.OrdinalIgnoreCase);
          609  +                connectionPool.ClearAllPools();
    85    610               }
    86         -
    87         -            if (fileName != null)
          611  +            else
    88    612               {
    89         -                Pool queue;
    90         -
    91         -                if (_connections.TryGetValue(fileName, out queue))
          613  +                lock (_syncRoot)
    92    614                   {
    93         -                    Queue<WeakReference> poolQueue = queue.Queue;
    94         -                    int count = (poolQueue != null) ? poolQueue.Count : 0;
          615  +                    foreach (KeyValuePair<string, PoolQueue> pair in _queueList)
          616  +                    {
          617  +                        if (pair.Value == null)
          618  +                            continue;
    95    619   
    96         -                    counts.Add(fileName, count);
    97         -                    totalCount += count;
          620  +                        Queue<WeakReference> poolQueue = pair.Value.Queue;
          621  +
          622  +                        while (poolQueue.Count > 0)
          623  +                        {
          624  +                            WeakReference connection = poolQueue.Dequeue();
          625  +
          626  +                            if (connection == null) continue;
          627  +
          628  +                            SQLiteConnectionHandle handle =
          629  +                                connection.Target as SQLiteConnectionHandle;
          630  +
          631  +                            if (handle != null)
          632  +                                handle.Dispose();
          633  +
          634  +                            GC.KeepAlive(handle);
          635  +                        }
          636  +
          637  +                        //
          638  +                        // NOTE: Keep track of the highest revision so we can
          639  +                        //       go one higher when we are finished.
          640  +                        //
          641  +                        if (_poolVersion <= pair.Value.PoolVersion)
          642  +                            _poolVersion = pair.Value.PoolVersion + 1;
          643  +                    }
          644  +
          645  +                    //
          646  +                    // NOTE: All pools are cleared and we have a new highest
          647  +                    //       version number to force all old version active
          648  +                    //       items to get discarded instead of going back to
          649  +                    //       the queue when they are closed.  We can get away
          650  +                    //       with this because we have pumped up the pool
          651  +                    //       version out of range of all active connections,
          652  +                    //       so they will all get discarded when they try to
          653  +                    //       put themselves back into their pools.
          654  +                    //
          655  +                    _queueList.Clear();
    98    656                   }
    99    657               }
          658  +        }
          659  +
          660  +        ///////////////////////////////////////////////////////////////////////
          661  +
          662  +        /// <summary>
          663  +        /// Adds a connection to the pool of those associated with the
          664  +        /// specified database file name.
          665  +        /// </summary>
          666  +        /// <param name="fileName">
          667  +        /// The database file name.
          668  +        /// </param>
          669  +        /// <param name="handle">
          670  +        /// The database connection handle.
          671  +        /// </param>
          672  +        /// <param name="version">
          673  +        /// The connection pool version at the point the database connection
          674  +        /// handle was received from the connection pool.  This is also the
          675  +        /// connection pool version that the database connection handle was
          676  +        /// created under.
          677  +        /// </param>
          678  +        internal static void Add(
          679  +            string fileName,
          680  +            SQLiteConnectionHandle handle,
          681  +            int version
          682  +            )
          683  +        {
          684  +            ISQLiteConnectionPool connectionPool = GetConnectionPool();
          685  +
          686  +            if (connectionPool != null)
          687  +            {
          688  +                connectionPool.Add(fileName, handle, version);
          689  +            }
   100    690               else
   101    691               {
   102         -                foreach (KeyValuePair<string, Pool> pair in _connections)
          692  +                lock (_syncRoot)
   103    693                   {
   104         -                    if (pair.Value == null)
   105         -                        continue;
          694  +                    //
          695  +                    // NOTE: If the queue does not exist in the pool, then it
          696  +                    //       must have been cleared sometime after the
          697  +                    //       connection was created.
          698  +                    //
          699  +                    PoolQueue queue;
   106    700   
   107         -                    Queue<WeakReference> poolQueue = pair.Value.Queue;
   108         -                    int count = (poolQueue != null) ? poolQueue.Count : 0;
          701  +                    if (_queueList.TryGetValue(fileName, out queue) &&
          702  +                        (version == queue.PoolVersion))
          703  +                    {
          704  +                        ResizePool(queue, true);
   109    705   
   110         -                    counts.Add(pair.Key, count);
   111         -                    totalCount += count;
          706  +                        Queue<WeakReference> poolQueue = queue.Queue;
          707  +                        if (poolQueue == null) return;
          708  +
          709  +                        poolQueue.Enqueue(new WeakReference(handle, false));
          710  +                        Interlocked.Increment(ref _poolClosed);
          711  +                    }
          712  +                    else
          713  +                    {
          714  +                        handle.Close();
          715  +                    }
          716  +
          717  +                    GC.KeepAlive(handle);
   112    718                   }
   113    719               }
   114    720           }
   115         -    }
   116         -
   117         -    /// <summary>
   118         -    /// Attempt to pull a pooled connection out of the queue for active duty
   119         -    /// </summary>
   120         -    /// <param name="fileName">The filename for a desired connection</param>
   121         -    /// <param name="maxPoolSize">The maximum size the connection pool for the filename can be</param>
   122         -    /// <param name="version">The pool version the returned connection will belong to</param>
   123         -    /// <returns>Returns NULL if no connections were available.  Even if none are, the poolversion will still be a valid pool version</returns>
   124         -    internal static SQLiteConnectionHandle Remove(string fileName, int maxPoolSize, out int version)
   125         -    {
   126         -        int localVersion;
   127         -        Queue<WeakReference> poolQueue;
   128         -
   129         -        //
   130         -        // NOTE: This lock cannot be held while checking the queue for available
   131         -        //       connections because other methods of this class are called from
   132         -        //       the GC finalizer thread and we use the WaitForPendingFinalizers
   133         -        //       method (below).  Holding this lock while calling that method
   134         -        //       would therefore result in a deadlock.  Instead, this lock is
   135         -        //       held only while a temporary copy of the queue is created, and
   136         -        //       if necessary, when committing changes back to that original
   137         -        //       queue prior to returning from this method.
   138         -        //
   139         -        lock (_connections)
          721  +
          722  +        ///////////////////////////////////////////////////////////////////////
          723  +
          724  +        /// <summary>
          725  +        /// Removes a connection from the pool of those associated with the
          726  +        /// specified database file name with the intent of using it to
          727  +        /// interact with the database.
          728  +        /// </summary>
          729  +        /// <param name="fileName">
          730  +        /// The database file name.
          731  +        /// </param>
          732  +        /// <param name="maxPoolSize">
          733  +        /// The new maximum size of the connection pool for the specified
          734  +        /// database file name.
          735  +        /// </param>
          736  +        /// <param name="version">
          737  +        /// The connection pool version associated with the returned database
          738  +        /// connection handle, if any.
          739  +        /// </param>
          740  +        /// <returns>
          741  +        /// The database connection handle associated with the specified
          742  +        /// database file name or null if it cannot be obtained.
          743  +        /// </returns>
          744  +        internal static SQLiteConnectionHandle Remove(
          745  +            string fileName,
          746  +            int maxPoolSize,
          747  +            out int version
          748  +            )
   140    749           {
   141         -            Pool queue;
          750  +            ISQLiteConnectionPool connectionPool = GetConnectionPool();
   142    751   
   143         -            // Default to the highest pool version
   144         -            version = _poolVersion;
   145         -
   146         -            // If we didn't find a pool for this file, create one even though it will be empty.
   147         -            // We have to do this here because otherwise calling ClearPool() on the file will not work for active connections
   148         -            // that have never seen the pool yet.
   149         -            if (_connections.TryGetValue(fileName, out queue) == false)
          752  +            if (connectionPool != null)
   150    753               {
   151         -                queue = new Pool(_poolVersion, maxPoolSize);
   152         -                _connections.Add(fileName, queue);
   153         -
   154         -                return null;
          754  +                return connectionPool.Remove(fileName, maxPoolSize,
          755  +                    out version) as SQLiteConnectionHandle;
   155    756               }
   156         -
   157         -            // We found a pool for this file, so use its version number
   158         -            version = localVersion = queue.PoolVersion;
   159         -            queue.MaxPoolSize = maxPoolSize;
   160         -
   161         -            ResizePool(queue, false);
   162         -
   163         -            // Try and get a pooled connection from the queue
   164         -            poolQueue = queue.Queue;
   165         -            if (poolQueue == null) return null;
   166         -
   167         -            //
   168         -            // NOTE: Temporarily tranfer the queue for this file into a local
   169         -            //       variable.  The queue for this file will be modified and
   170         -            //       then committed back to the real pool list (below) prior
   171         -            //       to returning from this method.
   172         -            //
   173         -            _connections.Remove(fileName);
   174         -            poolQueue = new Queue<WeakReference>(poolQueue);
   175         -        }
   176         -
   177         -        try
   178         -        {
   179         -            while (poolQueue.Count > 0)
          757  +            else
   180    758               {
   181         -                WeakReference cnn = poolQueue.Dequeue();
   182         -                if (cnn == null) continue;
          759  +                int localVersion;
          760  +                Queue<WeakReference> poolQueue;
   183    761   
   184         -                SQLiteConnectionHandle hdl = cnn.Target as SQLiteConnectionHandle;
   185         -                if (hdl == null) continue;
   186         -
          762  +                //
          763  +                // NOTE: This lock cannot be held while checking the queue for
          764  +                //       available connections because other methods of this
          765  +                //       class are called from the GC finalizer thread and we
          766  +                //       use the WaitForPendingFinalizers method (below).
          767  +                //       Holding this lock while calling that method would
          768  +                //       therefore result in a deadlock.  Instead, this lock
          769  +                //       is held only while a temporary copy of the queue is
          770  +                //       created, and if necessary, when committing changes
          771  +                //       back to that original queue prior to returning from
          772  +                //       this method.
   187    773                   //
   188         -                // BUGFIX: For ticket [996d13cd87], step #1.  After this point,
   189         -                //         make sure that the finalizer for the connection
   190         -                //         handle just obtained from the queue cannot START
   191         -                //         running (i.e. it may still be pending but it will no
   192         -                //         longer start after this point).
   193         -                //
   194         -                GC.SuppressFinalize(hdl);
          774  +                lock (_syncRoot)
          775  +                {
          776  +                    PoolQueue queue;
          777  +
          778  +                    //
          779  +                    // NOTE: Default to the highest pool version.
          780  +                    //
          781  +                    version = _poolVersion;
          782  +
          783  +                    //
          784  +                    // NOTE: If we didn't find a pool for this file, create one
          785  +                    //       even though it will be empty.  We have to do this
          786  +                    //       here because otherwise calling ClearPool() on the
          787  +                    //       file will not work for active connections that have
          788  +                    //       never seen the pool yet.
          789  +                    //
          790  +                    if (!_queueList.TryGetValue(fileName, out queue))
          791  +                    {
          792  +                        queue = new PoolQueue(_poolVersion, maxPoolSize);
          793  +                        _queueList.Add(fileName, queue);
          794  +
          795  +                        return null;
          796  +                    }
          797  +
          798  +                    //
          799  +                    // NOTE: We found a pool for this file, so use its version
          800  +                    //       number.
          801  +                    //
          802  +                    version = localVersion = queue.PoolVersion;
          803  +                    queue.MaxPoolSize = maxPoolSize;
          804  +
          805  +                    //
          806  +                    // NOTE: Now, resize the pool to the new maximum size, if
          807  +                    //       necessary.
          808  +                    //
          809  +                    ResizePool(queue, false);
          810  +
          811  +                    //
          812  +                    // NOTE: Try and get a pooled connection from the queue.
          813  +                    //
          814  +                    poolQueue = queue.Queue;
          815  +                    if (poolQueue == null) return null;
          816  +
          817  +                    //
          818  +                    // NOTE: Temporarily tranfer the queue for this file into
          819  +                    //       a local variable.  The queue for this file will
          820  +                    //       be modified and then committed back to the real
          821  +                    //       pool list (below) prior to returning from this
          822  +                    //       method.
          823  +                    //
          824  +                    _queueList.Remove(fileName);
          825  +                    poolQueue = new Queue<WeakReference>(poolQueue);
          826  +                }
   195    827   
   196    828                   try
   197    829                   {
   198         -                    //
   199         -                    // BUGFIX: For ticket [996d13cd87], step #2.  Now, we must wait
   200         -                    //         for all pending finalizers which have STARTED running
   201         -                    //         and have not yet COMPLETED.  This must be done just
   202         -                    //         in case the finalizer for the connection handle just
   203         -                    //         obtained from the queue has STARTED running at some
   204         -                    //         point before SuppressFinalize was called on it.
   205         -                    //
   206         -                    //         After this point, checking properties of the
   207         -                    //         connection handle (e.g. IsClosed) should work
   208         -                    //         reliably without having to worry that they will
   209         -                    //         (due to the finalizer) change out from under us.
   210         -                    //
   211         -                    GC.WaitForPendingFinalizers();
   212         -
   213         -                    //
   214         -                    // BUGFIX: For ticket [996d13cd87], step #3.  Next, verify that
   215         -                    //         the connection handle is actually valid and [still?]
   216         -                    //         not closed prior to actually returning it to our
   217         -                    //         caller.
   218         -                    //
   219         -                    if (!hdl.IsInvalid && !hdl.IsClosed)
          830  +                    while (poolQueue.Count > 0)
   220    831                       {
   221         -                        Interlocked.Increment(ref _poolOpened);
   222         -                        return hdl;
          832  +                        WeakReference connection = poolQueue.Dequeue();
          833  +
          834  +                        if (connection == null) continue;
          835  +
          836  +                        SQLiteConnectionHandle handle =
          837  +                            connection.Target as SQLiteConnectionHandle;
          838  +
          839  +                        if (handle == null) continue;
          840  +
          841  +                        //
          842  +                        // BUGFIX: For ticket [996d13cd87], step #1.  After
          843  +                        //         this point, make sure that the finalizer for
          844  +                        //         the connection handle just obtained from the
          845  +                        //         queue cannot START running (i.e. it may
          846  +                        //         still be pending but it will no longer start
          847  +                        //         after this point).
          848  +                        //
          849  +                        GC.SuppressFinalize(handle);
          850  +
          851  +                        try
          852  +                        {
          853  +                            //
          854  +                            // BUGFIX: For ticket [996d13cd87], step #2.  Now,
          855  +                            //         we must wait for all pending finalizers
          856  +                            //         which have STARTED running and have not
          857  +                            //         yet COMPLETED.  This must be done just
          858  +                            //         in case the finalizer for the connection
          859  +                            //         handle just obtained from the queue has
          860  +                            //         STARTED running at some point before
          861  +                            //         SuppressFinalize was called on it.
          862  +                            //
          863  +                            //         After this point, checking properties of
          864  +                            //         the connection handle (e.g. IsClosed)
          865  +                            //         should work reliably without having to
          866  +                            //         worry that they will (due to the
          867  +                            //         finalizer) change out from under us.
          868  +                            //
          869  +                            GC.WaitForPendingFinalizers();
          870  +
          871  +                            //
          872  +                            // BUGFIX: For ticket [996d13cd87], step #3.  Next,
          873  +                            //         verify that the connection handle is
          874  +                            //         actually valid and [still?] not closed
          875  +                            //         prior to actually returning it to our
          876  +                            //         caller.
          877  +                            //
          878  +                            if (!handle.IsInvalid && !handle.IsClosed)
          879  +                            {
          880  +                                Interlocked.Increment(ref _poolOpened);
          881  +                                return handle;
          882  +                            }
          883  +                        }
          884  +                        finally
          885  +                        {
          886  +                            //
          887  +                            // BUGFIX: For ticket [996d13cd87], step #4.  Next,
          888  +                            //         we must re-register the connection
          889  +                            //         handle for finalization now that we have
          890  +                            //         a strong reference to it (i.e. the
          891  +                            //         finalizer will not run at least until
          892  +                            //         the connection is subsequently closed).
          893  +                            //
          894  +                            GC.ReRegisterForFinalize(handle);
          895  +                        }
          896  +
          897  +                        GC.KeepAlive(handle);
   223    898                       }
   224    899                   }
   225    900                   finally
   226    901                   {
   227    902                       //
   228         -                    // BUGFIX: For ticket [996d13cd87], step #4.  Next, we must
   229         -                    //         re-register the connection handle for finalization
   230         -                    //         now that we have a strong reference to it (i.e. the
   231         -                    //         finalizer will not run at least until the connection
   232         -                    //         is subsequently closed).
          903  +                    // BUGFIX: For ticket [996d13cd87], step #5.  Finally,
          904  +                    //         commit any changes to the pool/queue for this
          905  +                    //         database file.
   233    906                       //
   234         -                    GC.ReRegisterForFinalize(hdl);
   235         -                }
   236         -
   237         -                GC.KeepAlive(hdl);
   238         -            }
   239         -        }
   240         -        finally
   241         -        {
   242         -            //
   243         -            // BUGFIX: For ticket [996d13cd87], step #5.  Finally, commit any
   244         -            //         changes to the pool/queue for this database file.
   245         -            //
   246         -            lock (_connections)
   247         -            {
   248         -                //
   249         -                // NOTE: We must check [again] if a pool exists for this file
   250         -                //       because one may have been added while the search for
   251         -                //       an available connection was in progress (above).
   252         -                //
   253         -                Pool queue;
   254         -                Queue<WeakReference> newPoolQueue;
   255         -                bool addPool;
   256         -
   257         -                if (_connections.TryGetValue(fileName, out queue))
   258         -                {
   259         -                    addPool = false;
   260         -                }
   261         -                else
   262         -                {
   263         -                    addPool = true;
   264         -                    queue = new Pool(localVersion, maxPoolSize);
          907  +                    lock (_syncRoot)
          908  +                    {
          909  +                        //
          910  +                        // NOTE: We must check [again] if a pool exists for
          911  +                        //       this file because one may have been added
          912  +                        //       while the search for an available connection
          913  +                        //       was in progress (above).
          914  +                        //
          915  +                        PoolQueue queue;
          916  +                        Queue<WeakReference> newPoolQueue;
          917  +                        bool addPool;
          918  +
          919  +                        if (_queueList.TryGetValue(fileName, out queue))
          920  +                        {
          921  +                            addPool = false;
          922  +                        }
          923  +                        else
          924  +                        {
          925  +                            addPool = true;
          926  +                            queue = new PoolQueue(localVersion, maxPoolSize);
          927  +                        }
          928  +
          929  +                        newPoolQueue = queue.Queue;
          930  +
          931  +                        while (poolQueue.Count > 0)
          932  +                            newPoolQueue.Enqueue(poolQueue.Dequeue());
          933  +
          934  +                        ResizePool(queue, false);
          935  +
          936  +                        if (addPool)
          937  +                            _queueList.Add(fileName, queue);
          938  +                    }
   265    939                   }
   266    940   
   267         -                newPoolQueue = queue.Queue;
          941  +                return null;
          942  +            }
          943  +        }
          944  +        #endregion
          945  +
          946  +        ///////////////////////////////////////////////////////////////////////
          947  +
          948  +        #region Private Helper Methods
          949  +        /// <summary>
          950  +        /// This method is used to obtain a reference to the custom connection
          951  +        /// pool implementation currently in use, if any.
          952  +        /// </summary>
          953  +        /// <returns>
          954  +        /// The custom connection pool implementation or null if the default
          955  +        /// connection pool implementation should be used.
          956  +        /// </returns>
          957  +        internal static ISQLiteConnectionPool GetConnectionPool()
          958  +        {
          959  +            lock (_syncRoot)
          960  +            {
          961  +                return _connectionPool;
          962  +            }
          963  +        }
          964  +
          965  +        ///////////////////////////////////////////////////////////////////////
          966  +
          967  +        /// <summary>
          968  +        /// This method is used to set the reference to the custom connection
          969  +        /// pool implementation to use, if any.
          970  +        /// </summary>
          971  +        /// <param name="connectionPool">
          972  +        /// The custom connection pool implementation to use or null if the
          973  +        /// default connection pool implementation should be used.
          974  +        /// </param>
          975  +        internal static void SetConnectionPool(
          976  +            ISQLiteConnectionPool connectionPool
          977  +            )
          978  +        {
          979  +            lock (_syncRoot)
          980  +            {
          981  +                _connectionPool = connectionPool;
          982  +            }
          983  +        }
          984  +
          985  +        ///////////////////////////////////////////////////////////////////////
          986  +
          987  +        /// <summary>
          988  +        /// We do not have to thread-lock anything in this function, because it
          989  +        /// is only called by other functions above which already take the lock.
          990  +        /// </summary>
          991  +        /// <param name="queue">
          992  +        /// The pool queue to resize.
          993  +        /// </param>
          994  +        /// <param name="add">
          995  +        /// If a function intends to add to the pool, this is true, which
          996  +        /// forces the resize to take one more than it needs from the pool.
          997  +        /// </param>
          998  +        private static void ResizePool(
          999  +            PoolQueue queue,
         1000  +            bool add
         1001  +            )
         1002  +        {
         1003  +            int target = queue.MaxPoolSize;
         1004  +
         1005  +            if (add && target > 0) target--;
         1006  +
         1007  +            Queue<WeakReference> poolQueue = queue.Queue;
         1008  +            if (poolQueue == null) return;
         1009  +
         1010  +            while (poolQueue.Count > target)
         1011  +            {
         1012  +                WeakReference connection = poolQueue.Dequeue();
         1013  +
         1014  +                if (connection == null) continue;
         1015  +
         1016  +                SQLiteConnectionHandle handle =
         1017  +                    connection.Target as SQLiteConnectionHandle;
   268   1018   
   269         -                while (poolQueue.Count > 0)
   270         -                    newPoolQueue.Enqueue(poolQueue.Dequeue());
         1019  +                if (handle != null)
         1020  +                    handle.Dispose();
   271   1021   
   272         -                ResizePool(queue, false);
   273         -
   274         -                if (addPool)
   275         -                    _connections.Add(fileName, queue);
         1022  +                GC.KeepAlive(handle);
   276   1023               }
   277   1024           }
   278         -
   279         -        return null;
         1025  +        #endregion
   280   1026       }
   281         -
   282         -    /// <summary>
   283         -    /// Clears out all pooled connections and rev's up the default pool version to force all old active objects
   284         -    /// not in the pool to get discarded rather than returned to their pools.
   285         -    /// </summary>
   286         -    internal static void ClearAllPools()
   287         -    {
   288         -      lock (_connections)
   289         -      {
   290         -        foreach (KeyValuePair<string, Pool> pair in _connections)
   291         -        {
   292         -          if (pair.Value == null)
   293         -            continue;
   294         -
   295         -          Queue<WeakReference> poolQueue = pair.Value.Queue;
   296         -
   297         -          while (poolQueue.Count > 0)
   298         -          {
   299         -            WeakReference cnn = poolQueue.Dequeue();
   300         -            if (cnn == null) continue;
   301         -            SQLiteConnectionHandle hdl = cnn.Target as SQLiteConnectionHandle;
   302         -            if (hdl != null)
   303         -            {
   304         -              hdl.Dispose();
   305         -            }
   306         -            GC.KeepAlive(hdl);
   307         -          }
   308         -          
   309         -          // Keep track of the highest revision so we can go one higher when we're finished
   310         -          if (_poolVersion <= pair.Value.PoolVersion)
   311         -            _poolVersion = pair.Value.PoolVersion + 1;
   312         -        }
   313         -        // All pools are cleared and we have a new highest version number to force all old version active items to get discarded
   314         -        // instead of going back to the queue when they are closed.
   315         -        // We can get away with this because we've pumped up the _poolVersion out of range of all active connections, so they
   316         -        // will all get discarded when they try to put themselves back in their pool.
   317         -        _connections.Clear();
   318         -      }
   319         -    }
   320         -
   321         -    /// <summary>
   322         -    /// Clear a given pool for a given filename.  Discards anything in the pool for the given file, and revs the pool
   323         -    /// version so current active objects on the old version of the pool will get discarded rather than be returned to the pool.
   324         -    /// </summary>
   325         -    /// <param name="fileName">The filename of the pool to clear</param>
   326         -    internal static void ClearPool(string fileName)
   327         -    {
   328         -      lock (_connections)
   329         -      {
   330         -        Pool queue;
   331         -        if (_connections.TryGetValue(fileName, out queue) == true)
   332         -        {
   333         -          queue.PoolVersion++;
   334         -
   335         -          Queue<WeakReference> poolQueue = queue.Queue;
   336         -          if (poolQueue == null) return;
   337         -
   338         -          while (poolQueue.Count > 0)
   339         -          {
   340         -            WeakReference cnn = poolQueue.Dequeue();
   341         -            if (cnn == null) continue;
   342         -            SQLiteConnectionHandle hdl = cnn.Target as SQLiteConnectionHandle;
   343         -            if (hdl != null)
   344         -            {
   345         -              hdl.Dispose();
   346         -            }
   347         -            GC.KeepAlive(hdl);
   348         -          }
   349         -        }
   350         -      }
   351         -    }
   352         -
   353         -    /// <summary>
   354         -    /// Return a connection to the pool for someone else to use.
   355         -    /// </summary>
   356         -    /// <param name="fileName">The filename of the pool to use</param>
   357         -    /// <param name="hdl">The connection handle to pool</param>
   358         -    /// <param name="version">The pool version the handle was created under</param>
   359         -    /// <remarks>
   360         -    /// If the version numbers don't match between the connection and the pool, then the handle is discarded.
   361         -    /// </remarks>
   362         -    internal static void Add(string fileName, SQLiteConnectionHandle hdl, int version)
   363         -    {
   364         -      lock (_connections)
   365         -      {
   366         -        // If the queue doesn't exist in the pool, then it must've been cleared sometime after the connection was created.
   367         -        Pool queue;
   368         -        if (_connections.TryGetValue(fileName, out queue) == true && version == queue.PoolVersion)
   369         -        {
   370         -          ResizePool(queue, true);
   371         -
   372         -          Queue<WeakReference> poolQueue = queue.Queue;
   373         -          if (poolQueue == null) return;
   374         -
   375         -          poolQueue.Enqueue(new WeakReference(hdl, false));
   376         -          Interlocked.Increment(ref _poolClosed);
   377         -        }
   378         -        else
   379         -        {
   380         -          hdl.Close();
   381         -        }
   382         -        GC.KeepAlive(hdl);
   383         -      }
   384         -    }
   385         -
   386         -    /// <summary>
   387         -    /// We don't have to thread-lock anything in this function, because it's only called by other functions above
   388         -    /// which already have a thread-safe lock.
   389         -    /// </summary>
   390         -    /// <param name="queue">The queue to resize</param>
   391         -    /// <param name="forAdding">If a function intends to add to the pool, this is true, which forces the resize
   392         -    /// to take one more than it needs from the pool</param>
   393         -    private static void ResizePool(Pool queue, bool forAdding)
   394         -    {
   395         -      int target = queue.MaxPoolSize;
   396         -
   397         -      if (forAdding && target > 0) target--;
   398         -
   399         -      Queue<WeakReference> poolQueue = queue.Queue;
   400         -      if (poolQueue == null) return;
   401         -
   402         -      while (poolQueue.Count > target)
   403         -      {
   404         -        WeakReference cnn = poolQueue.Dequeue();
   405         -        if (cnn == null) continue;
   406         -        SQLiteConnectionHandle hdl = cnn.Target as SQLiteConnectionHandle;
   407         -        if (hdl != null)
   408         -        {
   409         -          hdl.Dispose();
   410         -        }
   411         -        GC.KeepAlive(hdl);
   412         -      }
   413         -    }
   414         -  }
         1027  +    #endregion
   415   1028   }

Changes to Tests/common.eagle.

  1783   1783           # NOTE: Check the current build configuration.  This should normally
  1784   1784           #       be either "Debug" or "Release".
  1785   1785           #
  1786   1786           tputs $::test_channel \
  1787   1787               "---- checking for System.Data.SQLite build configuration... "
  1788   1788   
  1789   1789           set configuration [getBuildConfiguration]
  1790         -        addConstraint [appendArgs buildConfiguration $configuration]
         1790  +        addConstraint [appendArgs buildConfiguration. $configuration]
  1791   1791           tputs $::test_channel [appendArgs \" $configuration \"\n]
  1792   1792   
  1793   1793           #
  1794   1794           # NOTE: Try to setup an interrupt callback using the script debugger
  1795   1795           #       that will cancel all SQL queries in progress for all database
  1796   1796           #       connections known to this interpreter.
  1797   1797           #

Added Tests/tkt-393d954be0.eagle.

            1  +###############################################################################
            2  +#
            3  +# tkt-393d954be0.eagle --
            4  +#
            5  +# Written by Joe Mistachkin.
            6  +# Released to the public domain, use at your own risk!
            7  +#
            8  +###############################################################################
            9  +
           10  +package require Eagle
           11  +package require Eagle.Library
           12  +package require Eagle.Test
           13  +
           14  +runTestPrologue
           15  +
           16  +###############################################################################
           17  +
           18  +package require System.Data.SQLite.Test
           19  +runSQLiteTestPrologue
           20  +
           21  +###############################################################################
           22  +
           23  +runTest {test tkt-393d954be0-1.1 {custom connection pool} -body {
           24  +  set nullPool [object create -flags +NonPublic -alias \
           25  +      System.Data.SQLite.NullConnectionPool true]
           26  +
           27  +  object invoke System.Data.SQLite.SQLiteConnection ConnectionPool $nullPool
           28  +
           29  +  setupDb [set fileName tkt-393d954be0-1.1.db] "" "" "" "" "Pooling=True;"
           30  +
           31  +  set exists(0) [file exists [file join [getDatabaseDirectory] [file tail \
           32  +      $fileName]]]
           33  +
           34  +  cleanupDb $fileName
           35  +
           36  +  set exists(1) [file exists [file join [getDatabaseDirectory] [file tail \
           37  +      $fileName]]]
           38  +
           39  +  set counts null; set openCount 0; set closeCount 0; set totalCount 0
           40  +  object invoke -flags +NonPublic System.Data.SQLite.SQLiteConnectionPool \
           41  +      GetCounts $fileName counts openCount closeCount totalCount
           42  +
           43  +  object invoke -flags +NonPublic System.Data.SQLite.SQLiteConnectionPool \
           44  +      ClearPool $fileName
           45  +
           46  +  object invoke -flags +NonPublic System.Data.SQLite.SQLiteConnectionPool \
           47  +      ClearAllPools
           48  +
           49  +  list $exists(0) $exists(1) $counts $openCount $closeCount $totalCount \
           50  +      [object invoke $nullPool ToString]
           51  +} -cleanup {
           52  +  cleanupDb $fileName
           53  +
           54  +  unset -nocomplain db fileName exists counts openCount closeCount totalCount \
           55  +      nullPool
           56  +} -constraints {eagle monoBug28 buildConfiguration.Debug command.sql\
           57  +compile.DATA SQLite System.Data.SQLite} -match regexp -result [string map \
           58  +[list \n \r\n] {^True False \{\} 0 0 0\
           59  +\{Remove\(".*?\\tkt-393d954be0-1\.1\.db",\
           60  +100, 0\)
           61  +Add\(".*?\\tkt-393d954be0-1\.1\.db", -?\d+, 0\)
           62  +GetCounts\("tkt-393d954be0-1\.1\.db", , 0, 0, 0\)
           63  +ClearPool\("tkt-393d954be0-1\.1\.db"\)
           64  +ClearAllPools\(\)
           65  +\}$}]}
           66  +
           67  +###############################################################################
           68  +
           69  +runSQLiteTestEpilogue
           70  +runTestEpilogue

Changes to readme.htm.

   190    190       <b>1.0.85.0 - March XX, 2013 <font color="red">(release scheduled)</font></b>
   191    191   </p>
   192    192   <ul>
   193    193       <li>Updated to <a href="http://www.sqlite.org/src/info/trunk">SQLite 3.7.16</a>.</li>
   194    194       <li>Skip checking loaded assemblies for types tagged with the SQLiteFunction attribute when the No_SQLiteFunctions environment variable is set. Pursuant to [e4c8121f7b].</li>
   195    195       <li>Add HexPassword connection string property to work around the inability to include a literal semicolon in a connection string property value. Pursuant to [1c456ae75f].</li>
   196    196       <li>Add static Execute method to the SQLiteCommand class.</li>
          197  +    <li>Support custom connection pool implementations by adding the ISQLiteConnectionPool interface, the static SQLiteConnection.ConnectionPool property, and the static CreateHandle method in addition to modifying the SQLiteConnectionPool class. Pursuant to [393d954be0].</li>
   197    198       <li>Add public constructor to the SQLiteDataAdapter class that allows passing the parseViaFramework parameter to the SQLiteConnection constructor.</li>
   198    199       <li>When built with the CHECK_STATE compile-time option, skip throwing exceptions from the SQLiteDataReader class when the object is being disposed.</li>
   199    200       <li>Support automatic value conversions for columns with a declared type of BIGUINT, INTEGER8, INTEGER16, INTEGER32, INTEGER64, SMALLUINT, TINYSINT, UNSIGNEDINTEGER, UNSIGNEDINTEGER8, UNSIGNEDINTEGER16, UNSIGNEDINTEGER32, UNSIGNEDINTEGER64, INT8, INT16, INT32, INT64, UINT, UINT8, UINT16, UINT32, UINT64, or ULONG.</li>
   200    201       <li>Add BindUInt32AsInt64 connection flag to force binding of UInt32 values as Int64 instead. Pursuant to [c010fa6584].</li>
   201    202       <li>Remove AUTOINCREMENT from the column type name map.&nbsp;<b>** Potentially Incompatible Change **</b></li>
   202    203       <li>Avoid throwing overflow exceptions from the SQLite3.GetValue method for integral column types. Partial fix for [c010fa6584].&nbsp;<b>** Potentially Incompatible Change **</b></li>
   203    204       <li>Use the legacy connection closing algorithm when built with the INTEROP_LEGACY_CLOSE compile-time option.</li>

Changes to www/news.wiki.

     6      6       <b>1.0.85.0 - March XX, 2013 <font color="red">(release scheduled)</font></b>
     7      7   </p>
     8      8   <ul>
     9      9       <li>Updated to [http://www.sqlite.org/src/info/trunk|SQLite 3.7.16].</li>
    10     10       <li>Skip checking loaded assemblies for types tagged with the SQLiteFunction attribute when the No_SQLiteFunctions environment variable is set. Pursuant to [e4c8121f7b].</li>
    11     11       <li>Add HexPassword connection string property to work around the inability to include a literal semicolon in a connection string property value. Pursuant to [1c456ae75f].</li>
    12     12       <li>Add static Execute method to the SQLiteCommand class.</li>
           13  +    <li>Support custom connection pool implementations by adding the ISQLiteConnectionPool interface, the static SQLiteConnection.ConnectionPool property, and the static CreateHandle method in addition to modifying the SQLiteConnectionPool class. Pursuant to [393d954be0].</li>
    13     14       <li>Add public constructor to the SQLiteDataAdapter class that allows passing the parseViaFramework parameter to the SQLiteConnection constructor.</li>
    14     15       <li>When built with the CHECK_STATE compile-time option, skip throwing exceptions from the SQLiteDataReader class when the object is being disposed.</li>
    15     16       <li>Support automatic value conversions for columns with a declared type of BIGUINT, INTEGER8, INTEGER16, INTEGER32, INTEGER64, SMALLUINT, TINYSINT, UNSIGNEDINTEGER, UNSIGNEDINTEGER8, UNSIGNEDINTEGER16, UNSIGNEDINTEGER32, UNSIGNEDINTEGER64, INT8, INT16, INT32, INT64, UINT, UINT8, UINT16, UINT32, UINT64, or ULONG.</li>
    16     17       <li>Add BindUInt32AsInt64 connection flag to force binding of UInt32 values as Int64 instead. Pursuant to [c010fa6584].</li>
    17     18       <li>Remove AUTOINCREMENT from the column type name map.&nbsp;<b>** Potentially Incompatible Change **</b></li>
    18     19       <li>Avoid throwing overflow exceptions from the SQLite3.GetValue method for integral column types. Partial fix for [c010fa6584].&nbsp;<b>** Potentially Incompatible Change **</b></li>
    19     20       <li>Use the legacy connection closing algorithm when built with the INTEROP_LEGACY_CLOSE compile-time option.</li>