System.Data.SQLite

Check-in [7b3fe92dcb]
Login

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

Overview
Comment:Do not attempt to reset a connection if it has been closed. Also, prevent any potential GC race while resetting a connection. Keep track of the total number of connections successfully opened and closed from any of the pools. Enhance test for ticket [996d13cd87] to make sure that at least one connection was opened and closed from a connection pool.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 7b3fe92dcb994e0af39ec6937c70abda0f3d7030
User & Date: mistachkin 2012-05-03 13:44:30.959
Context
2012-05-03
15:11
Fix a couple more potential GC race conditions. check-in: ed16f61cf7 user: mistachkin tags: trunk
13:44
Do not attempt to reset a connection if it has been closed. Also, prevent any potential GC race while resetting a connection. Keep track of the total number of connections successfully opened and closed from any of the pools. Enhance test for ticket [996d13cd87] to make sure that at least one connection was opened and closed from a connection pool. check-in: 7b3fe92dcb user: mistachkin tags: trunk
13:04
Stop creating the CriticalHandle derived classes via implicit operator conversions. Simplify test case for ticket [996d13cd87]. check-in: 29b506224a user: mistachkin tags: trunk
Changes
Side-by-Side Diff Ignore Whitespace Patch
Changes to System.Data.SQLite/SQLite3.cs.
289
290
291
292
293
294
295


296
297
298

299

300
301
302
303
304
305
306
289
290
291
292
293
294
295
296
297
298
299
300
301

302
303
304
305
306
307
308
309







+
+



+
-
+







    {
      SQLiteConnectionPool.ClearPool(_fileName);
    }

    internal override int CountPool()
    {
        Dictionary<string, int> counts = null;
        int openCount = 0;
        int closeCount = 0;
        int totalCount = 0;

        SQLiteConnectionPool.GetCounts(_fileName,
            ref counts, ref openCount, ref closeCount,
            ref counts, ref totalCount);
            ref totalCount);

        return totalCount;
    }

    internal override void SetTimeout(int nTimeoutMS)
    {
      int n = UnsafeNativeMethods.sqlite3_busy_timeout(_sql, nTimeoutMS);
Changes to System.Data.SQLite/SQLiteBase.cs.
392
393
394
395
396
397
398

399
400
401
402
403
404
405
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406







+







            if (n > 0) throw new SQLiteException(n, GetLastError(hdl, db));
        }
    }

    internal static void ResetConnection(SQLiteConnectionHandle hdl, IntPtr db)
    {
        if ((hdl == null) || (db == IntPtr.Zero)) return;
        if (hdl.IsClosed || hdl.IsInvalid) return;
        lock (hdl)
        {
            IntPtr stmt = IntPtr.Zero;
            int n;
            do
            {
                stmt = UnsafeNativeMethods.sqlite3_next_stmt(db, stmt);
415
416
417
418
419
420
421

422
423
424
425
426
427
428
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430







+








            if (IsAutocommit(db) == false) // a transaction is pending on the connection
            {
                n = UnsafeNativeMethods.sqlite3_exec(db, ToUTF8("ROLLBACK"), IntPtr.Zero, IntPtr.Zero, out stmt);
                if (n > 0) throw new SQLiteException(n, GetLastError(hdl, db));
            }
        }
        GC.KeepAlive(hdl);
    }

    internal static bool IsAutocommit(IntPtr db)
    {
      if (db == IntPtr.Zero) return false;
      return (UnsafeNativeMethods.sqlite3_get_autocommit(db) == 1);
    }
Changes to System.Data.SQLite/SQLiteConnectionPool.cs.
1
2
3
4
5
6
7
8
9
10
11

12
13
14
15
16
17
18
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19











+







/********************************************************
 * ADO.NET 2.0 Data Provider for SQLite Version 3.X
 * Written by Robert Simpson (robert@blackcastlesoft.com)
 * 
 * Released to the public domain, use at your own risk!
 ********************************************************/

namespace System.Data.SQLite
{
  using System;
  using System.Collections.Generic;
  using System.Threading;

  internal static class SQLiteConnectionPool
  {
    /// <summary>
    /// Keeps track of connections made on a specified file.  The PoolVersion dictates whether old objects get
    /// returned to the pool or discarded when no longer in use.
    /// </summary>
35
36
37
38
39
40
41












42
43
44
45
46


47
48
49
50


51
52
53
54
55



56
57
58
59
60
61
62
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82







+
+
+
+
+
+
+
+
+
+
+
+





+
+




+
+





+
+
+







    private static SortedList<string, Pool> _connections = new SortedList<string, Pool>(StringComparer.OrdinalIgnoreCase);

    /// <summary>
    /// The default version number new pools will get
    /// </summary>
    private static int _poolVersion = 1;

    /// <summary>
    /// The number of connections successfully opened from any pool.
    /// This value is incremented by the Remove method.
    /// </summary>
    private static int _poolOpened = 0;

    /// <summary>
    /// The number of connections successfully closed from any pool.
    /// This value is incremented by the Add method.
    /// </summary>
    private static int _poolClosed = 0;

    /// <summary>
    /// Counts the number of pool entries matching the specified file name.
    /// </summary>
    /// <param name="fileName">The file name to match or null to match all files.</param>
    /// <param name="counts">The pool entry counts for each matching file.</param>
    /// <param name="openCount">The total number of connections successfully opened from any pool.</param>
    /// <param name="closeCount">The total number of connections successfully closed from any pool.</param>
    /// <param name="totalCount">The total number of pool entries for all matching files.</param>
    internal static void GetCounts(
        string fileName,
        ref Dictionary<string, int> counts,
        ref int openCount,
        ref int closeCount,
        ref int totalCount
        )
    {
        lock (_connections)
        {
            openCount = _poolOpened;
            closeCount = _poolClosed;

            if (counts == null)
            {
                counts = new Dictionary<string, int>(
                    StringComparer.OrdinalIgnoreCase);
            }

            if (fileName != null)
128
129
130
131
132
133
134

135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152





153

154
155

156
157
158
159
160
161
162
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178

179
180

181
182
183
184
185
186
187
188







+


















+
+
+
+
+
-
+

-
+








        while (poolQueue.Count > 0)
        {
          WeakReference cnn = poolQueue.Dequeue();
          SQLiteConnectionHandle hdl = cnn.Target as SQLiteConnectionHandle;
          if ((hdl != null) && !hdl.IsClosed && !hdl.IsInvalid)
          {
            Interlocked.Increment(ref _poolOpened);
            return hdl;
          }
          GC.KeepAlive(hdl);
        }
        return null;
      }
    }

    /// <summary>
    /// Clears out all pooled connections and rev's up the default pool version to force all old active objects
    /// not in the pool to get discarded rather than returned to their pools.
    /// </summary>
    internal static void ClearAllPools()
    {
      lock (_connections)
      {
        foreach (KeyValuePair<string, Pool> pair in _connections)
        {
          if (pair.Value == null)
            continue;

          Queue<WeakReference> poolQueue = pair.Value.Queue;

          while (pair.Value.Queue.Count > 0)
          while (poolQueue.Count > 0)
          {
            WeakReference cnn = pair.Value.Queue.Dequeue();
            WeakReference cnn = poolQueue.Dequeue();
            SQLiteConnectionHandle hdl = cnn.Target as SQLiteConnectionHandle;
            if (hdl != null)
            {
              hdl.Dispose();
            }
            GC.KeepAlive(hdl);
          }
223
224
225
226
227
228
229

230
231
232
233
234
235
236
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263







+







        {
          ResizePool(queue, true);

          Queue<WeakReference> poolQueue = queue.Queue;
          if (poolQueue == null) return;

          poolQueue.Enqueue(new WeakReference(hdl, false));
          Interlocked.Increment(ref _poolClosed);
          GC.KeepAlive(hdl);
        }
        else
        {
          hdl.Close();
        }
      }
Changes to Tests/tkt-996d13cd87.eagle.
52
53
54
55
56
57
58






59
60
61
62
63
64
65
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71







+
+
+
+
+
+







          int errors = 0;

          //
          // NOTE: This is the total number of test threads to create.
          //
          int count = 100;

          //
          // NOTE: This is the total number of times we should force a full
          //       garbage collection.
          //
          int gcCount = 2;

          //
          // NOTE: Create a random number generator suitable for waiting a
          //       random number of milliseconds between each attempt to
          //       cause the race condition on a given thread.
          //
          Random random = new Random();

129
130
131
132
133
134
135


136
137



138
139
140
141
142
143
144
135
136
137
138
139
140
141
142
143


144
145
146
147
148
149
150
151
152
153







+
+
-
-
+
+
+







                //
                goEvent.WaitOne();

                //
                // NOTE: Wait a random number of milliseconds before forcing a
                //       full garbage collection.
                //
                for (int index = 0; index < gcCount; index++)
                {
                Thread.Sleep(random.Next(0, 1000));
                GC.GetTotalMemory(true);
                  Thread.Sleep(random.Next(0, 1000));
                  GC.GetTotalMemory(true);
                }
              }
              catch (Exception e)
              {
                Interlocked.Increment(ref errors);
                Trace.WriteLine(e);
              }
            };
208
209
210
211
212
213
214
215





216
217
218
219
220
221
222
223
224
225


226
227
228
229
230
217
218
219
220
221
222
223

224
225
226
227
228
229
230
231
232
233
234
235
236
237

238
239
240
241
242
243
244







-
+
+
+
+
+









-
+
+





    }
  }] true true true results errors System.Data.SQLite.dll]

  list $code $results \
      [expr {[info exists errors] ? $errors : ""}] \
      [expr {$code eq "Ok" ? [catch {
        object invoke _Dynamic${id}.Test${id} Main
      } result] : [set result ""]}] $result
      } result] : [set result ""]}] $result \
      [expr {[object invoke -flags +NonPublic \
          System.Data.SQLite.SQLiteConnectionPool _poolOpened] > 0}] \
      [expr {[object invoke -flags +NonPublic \
          System.Data.SQLite.SQLiteConnectionPool _poolClosed] > 0}]
} -cleanup {
  object invoke System.Data.SQLite.SQLiteConnection ClearAllPools
  object invoke GC GetTotalMemory true

  cleanupDb $fileName

  unset -nocomplain result results errors code sql dataSource id db fileName
} -constraints \
{eagle monoBug28 command.sql compile.DATA SQLite System.Data.SQLite} -match \
regexp -result {^Ok System#CodeDom#Compiler#CompilerResults#\d+ \{\} 0 \d+$}}
regexp -result {^Ok System#CodeDom#Compiler#CompilerResults#\d+ \{\} 0 \d+ True\
True$}}

###############################################################################

runSQLiteTestEpilogue
runTestEpilogue