System.Data.SQLite
Check-in [a27556f61d]
Not logged in

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

Overview
Comment:Support asynchronous completion of distributed transactions, fix for [5cee5409f8]. Add experimental WaitForEnlistmentReset method to the SQLiteConnection class, pursuant to [7e1dd697dc].
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:a27556f61daf7e5470556c9a843899c529643733
User & Date: mistachkin 2018-01-28 05:28:38
Context
2018-01-28
05:38
Update the VsWhere tool in externals to the 2.3.2 release. check-in: 1e2a7d22d2 user: mistachkin tags: trunk
05:28
Support asynchronous completion of distributed transactions, fix for [5cee5409f8]. Add experimental WaitForEnlistmentReset method to the SQLiteConnection class, pursuant to [7e1dd697dc]. check-in: a27556f61d user: mistachkin tags: trunk
05:27
Update version history docs. Closed-Leaf check-in: 81d4c53f90 user: mistachkin tags: tkt-5cee5409f8
2018-01-26
02:22
Further debugging enhancements. check-in: 0b0bd83ddd user: mistachkin tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to Doc/Extra/Provider/version.html.

43
44
45
46
47
48
49


50
51
52
53
54
55
56
    <div id="mainSection">
    <div id="mainBody">
    <h1 class="heading">Version History</h1>
    <p><b>1.0.107.0 - January XX, 2018 <font color="red">(release scheduled)</font></b></p>
    <ul>
      <li>Updated to <a href="https://www.sqlite.org/draft/releaselog/3_22_0.html">SQLite 3.22.0</a>.</li>
      <li>Improve performance of type name lookups by removing superfluous locking and string creation.</li>


      <li>Fix some internal memory accounting present only in the debug build.</li>
      <li>Make sure inbound native delegates are unhooked before adding a connection to the pool. Fix for <a href="https://system.data.sqlite.org/index.html/info/0e48e80333">[0e48e80333]</a>.</li>
      <li>Add preliminary support for the .NET Framework 4.7.1.</li>
      <li>Updates to internal DbType mapping related lookup tables. Pursuant to <a href="https://system.data.sqlite.org/index.html/info/a799e3978f">[a799e3978f]</a>.</li>
    </ul>
    <p><b>1.0.106.0 - November 2, 2017</b></p>
    <ul>







>
>







43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
    <div id="mainSection">
    <div id="mainBody">
    <h1 class="heading">Version History</h1>
    <p><b>1.0.107.0 - January XX, 2018 <font color="red">(release scheduled)</font></b></p>
    <ul>
      <li>Updated to <a href="https://www.sqlite.org/draft/releaselog/3_22_0.html">SQLite 3.22.0</a>.</li>
      <li>Improve performance of type name lookups by removing superfluous locking and string creation.</li>
      <li>Support asynchronous completion of distributed transactions. Fix for <a href="https://system.data.sqlite.org/index.html/info/5cee5409f8">[5cee5409f8]</a>.</li>
      <li>Add experimental WaitForEnlistmentReset method to the SQLiteConnection class. Pursuant to <a href="https://system.data.sqlite.org/index.html/info/7e1dd697dc">[7e1dd697dc]</a>.</li>
      <li>Fix some internal memory accounting present only in the debug build.</li>
      <li>Make sure inbound native delegates are unhooked before adding a connection to the pool. Fix for <a href="https://system.data.sqlite.org/index.html/info/0e48e80333">[0e48e80333]</a>.</li>
      <li>Add preliminary support for the .NET Framework 4.7.1.</li>
      <li>Updates to internal DbType mapping related lookup tables. Pursuant to <a href="https://system.data.sqlite.org/index.html/info/a799e3978f">[a799e3978f]</a>.</li>
    </ul>
    <p><b>1.0.106.0 - November 2, 2017</b></p>
    <ul>

Changes to Setup/data/verify.lst.

861
862
863
864
865
866
867

868
869
870
871
872
873
874
  Tests/tkt-5251bd0878.eagle
  Tests/tkt-53633bbe39.eagle
  Tests/tkt-544dba0a2f.eagle
  Tests/tkt-5535448538.eagle
  Tests/tkt-56b42d99c1.eagle
  Tests/tkt-58ed318f2f.eagle
  Tests/tkt-59edc1018b.eagle

  Tests/tkt-6434e23a0f.eagle
  Tests/tkt-647d282d11.eagle
  Tests/tkt-69cf6e5dc8.eagle
  Tests/tkt-6c6ecccc5f.eagle
  Tests/tkt-71bedaca19.eagle
  Tests/tkt-72905c9a77.eagle
  Tests/tkt-74542e702e.eagle







>







861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
  Tests/tkt-5251bd0878.eagle
  Tests/tkt-53633bbe39.eagle
  Tests/tkt-544dba0a2f.eagle
  Tests/tkt-5535448538.eagle
  Tests/tkt-56b42d99c1.eagle
  Tests/tkt-58ed318f2f.eagle
  Tests/tkt-59edc1018b.eagle
  Tests/tkt-5cee5409f8.eagle
  Tests/tkt-6434e23a0f.eagle
  Tests/tkt-647d282d11.eagle
  Tests/tkt-69cf6e5dc8.eagle
  Tests/tkt-6c6ecccc5f.eagle
  Tests/tkt-71bedaca19.eagle
  Tests/tkt-72905c9a77.eagle
  Tests/tkt-74542e702e.eagle

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

14
15
16
17
18
19
20

21
22
23
24
25
26
27
....
1476
1477
1478
1479
1480
1481
1482






1483
1484
1485
1486
1487
1488
1489
....
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
....
2916
2917
2918
2919
2920
2921
2922





2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944



2945

2946
2947
2948
2949

2950
2951
2952
2953
2954
2955
2956
....
3393
3394
3395
3396
3397
3398
3399
3400
3401


3402
3403
3404
3405
3406
3407
3408
3409
3410
3411
3412
3413
3414
3415
3416
3417
3418
3419
3420
3421




















































































































3422
3423
3424
3425
3426
3427
3428
  using System.Collections.Generic;
  using System.Globalization;
  using System.ComponentModel;
  using System.Reflection;
  using System.Runtime.InteropServices;
  using System.IO;
  using System.Text;


  /////////////////////////////////////////////////////////////////////////////////////////////////

  /// <summary>
  /// This class represents a single value to be returned
  /// from the <see cref="SQLiteDataReader" /> class via
  /// its <see cref="SQLiteDataReader.GetBlob" />,
................................................................................

    /// <summary>
    /// The default isolation level for new transactions
    /// </summary>
    private IsolationLevel _defaultIsolation;

#if !PLATFORM_COMPACTFRAMEWORK






    /// <summary>
    /// Whether or not the connection is enlisted in a distrubuted transaction
    /// </summary>
    internal SQLiteEnlistment _enlistment;
#endif

    /// <summary>
................................................................................
                // NOTE: If we need to retry the previous operation, wait for
                //       the number of milliseconds specified by our caller
                //       unless the caller used a negative number, in that case
                //       skip sleeping at all because we do not want to block
                //       this thread forever.
                //
                if (retry && (retryMilliseconds >= 0))
                    System.Threading.Thread.Sleep(retryMilliseconds);

                //
                // NOTE: There is no point in calling the native API to copy
                //       zero pages as it does nothing; therefore, stop now.
                //
                if (pages == 0)
                    break;
................................................................................
      OnChanged(this, new ConnectionEventArgs(
          SQLiteConnectionEventType.Closing, null, null, null, null, null,
          null, null));

      if (_sql != null)
      {
#if !PLATFORM_COMPACTFRAMEWORK





        if (_enlistment != null)
        {
          // If the connection is enlisted in a transaction scope and the scope is still active,
          // we cannot truly shut down this connection until the scope has completed.  Therefore make a
          // hidden connection temporarily to hold open the connection until the scope has completed.
          SQLiteConnection cnn = new SQLiteConnection();

#if DEBUG
          cnn._debugString = HelperMethods.StringFormat(
              CultureInfo.InvariantCulture,
              "closeThreadId = {0}, {1}{2}{2}{3}",
              HelperMethods.GetThreadId(), _sql,
              Environment.NewLine, _debugString);
#endif

          cnn._sql = _sql;
          cnn._transactionLevel = _transactionLevel;
          cnn._transactionSequence = _transactionSequence;
          cnn._enlistment = _enlistment;
          cnn._connectionState = _connectionState;
          cnn._version = _version;




          cnn._enlistment._transaction._cnn = cnn;

          cnn._enlistment._disposeConnection = true;

          _sql = null;
          _enlistment = null;

        }
#endif
        if (_sql != null)
        {
          _sql.Close(_disposing);
          _sql = null;
        }
................................................................................
#if !PLATFORM_COMPACTFRAMEWORK
    /// <summary>
    /// Manual distributed transaction enlistment support
    /// </summary>
    /// <param name="transaction">The distributed transaction to enlist in</param>
    public override void EnlistTransaction(System.Transactions.Transaction transaction)
    {
      CheckDisposed();



      if (_enlistment != null && transaction == _enlistment._scope)
        return;
      else if (_enlistment != null)
        throw new ArgumentException("Already enlisted in a transaction");

      if (_transactionLevel > 0 && transaction != null)
        throw new ArgumentException("Unable to enlist in transaction, a local transaction already exists");
      else if (transaction == null)
        throw new ArgumentNullException("Unable to enlist in transaction, it is null");

      bool strictEnlistment = ((_flags & SQLiteConnectionFlags.StrictEnlistment) ==
          SQLiteConnectionFlags.StrictEnlistment);

      _enlistment = new SQLiteEnlistment(this, transaction,
          GetFallbackDefaultIsolationLevel(), strictEnlistment,
          strictEnlistment);

      OnChanged(this, new ConnectionEventArgs(
          SQLiteConnectionEventType.EnlistTransaction, null, null, null, null,
          null, null, new object[] { _enlistment }));




















































































































    }
#endif

    /// <summary>
    /// Looks for a key in the array of key/values of the parameter string.  If not found, return the specified default value
    /// </summary>
    /// <param name="items">The list to look in</param>







>







 







>
>
>
>
>
>







 







|







 







>
>
>
>
>
|
|
|
|
|
|


|
|
|
|
|


|
|
|
|
|
|

>
>
>
|
>
|

|
<
>







 







|

>
>
|
|
|
|

|
|
|
|

|
|

|
|
|

|
|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
....
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
....
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
....
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964

2965
2966
2967
2968
2969
2970
2971
2972
....
3409
3410
3411
3412
3413
3414
3415
3416
3417
3418
3419
3420
3421
3422
3423
3424
3425
3426
3427
3428
3429
3430
3431
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
3451
3452
3453
3454
3455
3456
3457
3458
3459
3460
3461
3462
3463
3464
3465
3466
3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482
3483
3484
3485
3486
3487
3488
3489
3490
3491
3492
3493
3494
3495
3496
3497
3498
3499
3500
3501
3502
3503
3504
3505
3506
3507
3508
3509
3510
3511
3512
3513
3514
3515
3516
3517
3518
3519
3520
3521
3522
3523
3524
3525
3526
3527
3528
3529
3530
3531
3532
3533
3534
3535
3536
3537
3538
3539
3540
3541
3542
3543
3544
3545
3546
3547
3548
3549
3550
3551
3552
3553
3554
3555
3556
3557
3558
3559
3560
3561
3562
  using System.Collections.Generic;
  using System.Globalization;
  using System.ComponentModel;
  using System.Reflection;
  using System.Runtime.InteropServices;
  using System.IO;
  using System.Text;
  using System.Threading;

  /////////////////////////////////////////////////////////////////////////////////////////////////

  /// <summary>
  /// This class represents a single value to be returned
  /// from the <see cref="SQLiteDataReader" /> class via
  /// its <see cref="SQLiteDataReader.GetBlob" />,
................................................................................

    /// <summary>
    /// The default isolation level for new transactions
    /// </summary>
    private IsolationLevel _defaultIsolation;

#if !PLATFORM_COMPACTFRAMEWORK
    /// <summary>
    /// This object is used with lock statements to synchronize access to the
    /// <see cref="_enlistment" /> field, below.
    /// </summary>
    internal readonly object _enlistmentSyncRoot = new object();

    /// <summary>
    /// Whether or not the connection is enlisted in a distrubuted transaction
    /// </summary>
    internal SQLiteEnlistment _enlistment;
#endif

    /// <summary>
................................................................................
                // NOTE: If we need to retry the previous operation, wait for
                //       the number of milliseconds specified by our caller
                //       unless the caller used a negative number, in that case
                //       skip sleeping at all because we do not want to block
                //       this thread forever.
                //
                if (retry && (retryMilliseconds >= 0))
                    Thread.Sleep(retryMilliseconds);

                //
                // NOTE: There is no point in calling the native API to copy
                //       zero pages as it does nothing; therefore, stop now.
                //
                if (pages == 0)
                    break;
................................................................................
      OnChanged(this, new ConnectionEventArgs(
          SQLiteConnectionEventType.Closing, null, null, null, null, null,
          null, null));

      if (_sql != null)
      {
#if !PLATFORM_COMPACTFRAMEWORK
        lock (_enlistmentSyncRoot) /* TRANSACTIONAL */
        {
          SQLiteEnlistment enlistment = _enlistment;
          _enlistment = null;

          if (enlistment != null)
          {
            // If the connection is enlisted in a transaction scope and the scope is still active,
            // we cannot truly shut down this connection until the scope has completed.  Therefore make a
            // hidden connection temporarily to hold open the connection until the scope has completed.
            SQLiteConnection cnn = new SQLiteConnection();

#if DEBUG
            cnn._debugString = HelperMethods.StringFormat(
                CultureInfo.InvariantCulture,
                "closeThreadId = {0}, {1}{2}{2}{3}",
                HelperMethods.GetThreadId(), _sql,
                Environment.NewLine, _debugString);
#endif

            cnn._sql = _sql;
            cnn._transactionLevel = _transactionLevel;
            cnn._transactionSequence = _transactionSequence;
            cnn._enlistment = enlistment;
            cnn._connectionState = _connectionState;
            cnn._version = _version;

            SQLiteTransaction transaction = enlistment._transaction;

            if (transaction != null)
                transaction._cnn = cnn;

            enlistment._disposeConnection = true;

            _sql = null;

          }
        }
#endif
        if (_sql != null)
        {
          _sql.Close(_disposing);
          _sql = null;
        }
................................................................................
#if !PLATFORM_COMPACTFRAMEWORK
    /// <summary>
    /// Manual distributed transaction enlistment support
    /// </summary>
    /// <param name="transaction">The distributed transaction to enlist in</param>
    public override void EnlistTransaction(System.Transactions.Transaction transaction)
    {
        CheckDisposed();

        lock (_enlistmentSyncRoot) /* TRANSACTIONAL */
        {
            if (_enlistment != null && transaction == _enlistment._scope)
                return;
            else if (_enlistment != null)
                throw new ArgumentException("Already enlisted in a transaction");

            if (_transactionLevel > 0 && transaction != null)
                throw new ArgumentException("Unable to enlist in transaction, a local transaction already exists");
            else if (transaction == null)
                throw new ArgumentNullException("Unable to enlist in transaction, it is null");

            bool strictEnlistment = ((_flags & SQLiteConnectionFlags.StrictEnlistment) ==
                SQLiteConnectionFlags.StrictEnlistment);

            _enlistment = new SQLiteEnlistment(this, transaction,
                GetFallbackDefaultIsolationLevel(), strictEnlistment,
                strictEnlistment);

            OnChanged(this, new ConnectionEventArgs(
                SQLiteConnectionEventType.EnlistTransaction, null, null, null, null,
                null, null, new object[] { _enlistment }));
        }
    }

    /// <summary>
    /// <![CDATA[<b>]]>EXPERIMENTAL<![CDATA[</b>]]>
    /// Waits for the enlistment associated with this connection to be reset.
    /// </summary>
    /// <param name="timeoutMilliseconds">
    /// The approximate maximum number of milliseconds to wait before timing
    /// out the wait operation.
    /// </param>
    /// <returns>
    /// Non-zero if the enlistment assciated with this connection was reset;
    /// otherwise, zero.  It should be noted that this method returning a
    /// non-zero value does not necessarily guarantee that the connection
    /// can enlist in a new transaction (i.e. due to potentical race with
    /// other threads); therefore, callers should generally use try/catch
    /// when calling the <see cref="EnlistTransaction" /> method.
    /// </returns>
    public bool WaitForEnlistmentReset(
        int timeoutMilliseconds
        )
    {
        CheckDisposed();

        if (timeoutMilliseconds < 0)
            throw new ArgumentException("timeout cannot be negative");

        const int defaultMilliseconds = 100;
        int sleepMilliseconds;

        if (timeoutMilliseconds == 0)
        {
            sleepMilliseconds = 0;
        }
        else
        {
            sleepMilliseconds = Math.Min(
                timeoutMilliseconds / 10, defaultMilliseconds);

            if (sleepMilliseconds == 0)
                sleepMilliseconds = defaultMilliseconds;
        }

        DateTime start = DateTime.UtcNow;

        while (true)
        {
            //
            // NOTE: Attempt to acquire the necessary lock without blocking.
            //       This method will treat a failure to obtain the lock the
            //       same as the enlistment not being reset yet.  Both will
            //       advance toward the timeout.
            //
            bool locked = Monitor.TryEnter(_enlistmentSyncRoot);

            try
            {
                if (locked)
                {
                    //
                    // NOTE: Is there still an enlistment?  If not, we are
                    //       done.  There is a potential race condition in
                    //       the caller if another thread is able to setup
                    //       a new enlistment at any point prior to our
                    //       caller fully dealing with the result of this
                    //       method.  However, that should generally never
                    //       happen because this class is not intended to
                    //       be used by multiple concurrent threads, with
                    //       the notable exception of an active enlistment
                    //       being asynchronously committed or rolled back
                    //       by the .NET Framework.
                    //
                    if (_enlistment == null)
                        return true;
                }
            }
            finally
            {
                if (locked)
                {
                    Monitor.Exit(_enlistmentSyncRoot);
                    locked = false;
                }
            }

            //
            // NOTE: A timeout value of zero is special.  It means never
            //       sleep.
            //
            if (sleepMilliseconds == 0)
                return false;

            //
            // NOTE: How much time has elapsed since we first starting
            //       waiting?
            //
            DateTime now = DateTime.UtcNow;
            TimeSpan elapsed = now.Subtract(start);

            //
            // NOTE: Are we done wait?
            //
            double totalMilliseconds = elapsed.TotalMilliseconds;

            if ((totalMilliseconds < 0) || /* Time went backward? */
                (totalMilliseconds >= (double)timeoutMilliseconds))
            {
                return false;
            }

            //
            // NOTE: Sleep for a bit and then try again.
            //
            Thread.Sleep(sleepMilliseconds);
        }
    }
#endif

    /// <summary>
    /// Looks for a key in the array of key/values of the parameter string.  If not found, return the specified default value
    /// </summary>
    /// <param name="items">The list to look in</param>

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

102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
...
180
181
182
183
184
185
186
187
188
189
190
191
192
193





















194
195
196
197




198
199
200
201
202
203
204
205
206
207
208
209
210
...
223
224
225
226
227
228
229
230
231
232
233
234
235
236





















237





238
239
240
241
242
243
244
245
246
247
248
        return defaultIsolationLevel;
    }

    ///////////////////////////////////////////////////////////////////////////

    private void Cleanup(SQLiteConnection cnn)
    {
        if (_disposeConnection)
            cnn.Dispose();

        _transaction = null;
        _scope = null;
    }
    #endregion

................................................................................
    #endregion

    ///////////////////////////////////////////////////////////////////////////

    #region IEnlistmentNotification Members
    public void Commit(Enlistment enlistment)
    {
      CheckDisposed();

      SQLiteConnection cnn = _transaction.Connection;
      cnn._enlistment = null;

      try
      {





















        _transaction.IsValid(true);
        _transaction.Connection._transactionLevel = 1;
        _transaction.Commit();





        enlistment.Done();
      }
      finally
      {
        Cleanup(cnn);
      }
    }

    ///////////////////////////////////////////////////////////////////////////

    public void InDoubt(Enlistment enlistment)
    {
      CheckDisposed();
................................................................................
        preparingEnlistment.Prepared();
    }

    ///////////////////////////////////////////////////////////////////////////

    public void Rollback(Enlistment enlistment)
    {
      CheckDisposed();

      SQLiteConnection cnn = _transaction.Connection;
      cnn._enlistment = null;

      try
      {





















        _transaction.Rollback();





        enlistment.Done();
      }
      finally
      {
        Cleanup(cnn);
      }
    }
    #endregion
  }
}
#endif // !PLATFORM_COMPACT_FRAMEWORK







|







 







|

|
<

|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
|
|

>
>
>
>
|
|
|
|
|
|







 







|

|
<

|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>
|
|
|
|
|
|





102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
...
180
181
182
183
184
185
186
187
188
189

190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
...
247
248
249
250
251
252
253
254
255
256

257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
        return defaultIsolationLevel;
    }

    ///////////////////////////////////////////////////////////////////////////

    private void Cleanup(SQLiteConnection cnn)
    {
        if (_disposeConnection && (cnn != null))
            cnn.Dispose();

        _transaction = null;
        _scope = null;
    }
    #endregion

................................................................................
    #endregion

    ///////////////////////////////////////////////////////////////////////////

    #region IEnlistmentNotification Members
    public void Commit(Enlistment enlistment)
    {
        CheckDisposed();

        SQLiteConnection cnn = null;


        try
        {
            while (true)
            {
                cnn = _transaction.Connection;

                if (cnn == null)
                    break;

                lock (cnn._enlistmentSyncRoot) /* TRANSACTIONAL */
                {
                    //
                    // NOTE: This check is necessary to detect the case where
                    //       the SQLiteConnection.Close() method changes the
                    //       connection associated with our transaction (i.e.
                    //       to avoid a race (condition) between grabbing the
                    //       Connection property and locking its enlistment).
                    //
                    if (!Object.ReferenceEquals(cnn, _transaction.Connection))
                        continue;

                    cnn._enlistment = null;

                    _transaction.IsValid(true); /* throw */
                    cnn._transactionLevel = 1;
                    _transaction.Commit();

                    break;
                }
            }

            enlistment.Done();
        }
        finally
        {
            Cleanup(cnn);
        }
    }

    ///////////////////////////////////////////////////////////////////////////

    public void InDoubt(Enlistment enlistment)
    {
      CheckDisposed();
................................................................................
        preparingEnlistment.Prepared();
    }

    ///////////////////////////////////////////////////////////////////////////

    public void Rollback(Enlistment enlistment)
    {
        CheckDisposed();

        SQLiteConnection cnn = null;


        try
        {
            while (true)
            {
                cnn = _transaction.Connection;

                if (cnn == null)
                    break;

                lock (cnn._enlistmentSyncRoot) /* TRANSACTIONAL */
                {
                    //
                    // NOTE: This check is necessary to detect the case where
                    //       the SQLiteConnection.Close() method changes the
                    //       connection associated with our transaction (i.e.
                    //       to avoid a race (condition) between grabbing the
                    //       Connection property and locking its enlistment).
                    //
                    if (!Object.ReferenceEquals(cnn, _transaction.Connection))
                        continue;

                    cnn._enlistment = null;

                    _transaction.Rollback();

                    break;
                }
            }

            enlistment.Done();
        }
        finally
        {
            Cleanup(cnn);
        }
    }
    #endregion
  }
}
#endif // !PLATFORM_COMPACT_FRAMEWORK

Added Tests/tkt-5cee5409f8.eagle.























































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
###############################################################################
#
# tkt-5cee5409f8.eagle --
#
# Written by Joe Mistachkin.
# Released to the public domain, use at your own risk!
#
###############################################################################

package require Eagle
package require Eagle.Library
package require Eagle.Test

runTestPrologue

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

package require System.Data.SQLite.Test
runSQLiteTestPrologue

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

runTest {test tkt-5cee5409f8-1.1 {asynchronous transaction handling} -setup {
  setupDb [set fileName tkt-5cee5409f8-1.1.db]
} -body {
  sql execute $db "CREATE TABLE t1(x INTEGER);"

  set id [object invoke Interpreter.GetActive NextId]
  set dataSource [file join [getDatabaseDirectory] $fileName]

  unset -nocomplain results errors

  set code [compileCSharpWith [subst {
    using System;
    using System.Data.SQLite;
    using System.Threading;
    using System.Transactions;

    namespace _Dynamic${id}
    {
      public static class Test${id}
      {
        #region Private EnlistmentNotification Class
        private sealed class EnlistmentNotification : IEnlistmentNotification
        {
          #region Private Data
          private bool forceRollback;
          #endregion

          /////////////////////////////////////////////////////////////////////

          #region Private Constructors
          private EnlistmentNotification(bool forceRollback)
          {
            this.forceRollback = forceRollback;
          }
          #endregion

          /////////////////////////////////////////////////////////////////////

          #region IEnlistmentNotification Members
          public void Commit(Enlistment enlistment)
          {
            enlistment.Done();
          }

          /////////////////////////////////////////////////////////////////////

          public void InDoubt(Enlistment enlistment)
          {
            enlistment.Done();
          }

          /////////////////////////////////////////////////////////////////////

          public void Prepare(PreparingEnlistment preparingEnlistment)
          {
            if (forceRollback)
              preparingEnlistment.ForceRollback();
            else
              preparingEnlistment.Prepared();
          }

          /////////////////////////////////////////////////////////////////////

          public void Rollback(Enlistment enlistment)
          {
            enlistment.Done();
          }
          #endregion

          /////////////////////////////////////////////////////////////////////

          #region Public Static Methods
          public static void UseDistributedTransaction(bool forceRollback)
          {
            Transaction.Current.EnlistDurable(
                Guid.NewGuid(), new EnlistmentNotification(forceRollback),
                EnlistmentOptions.None);
          }
          #endregion
        }
        #endregion

        ///////////////////////////////////////////////////////////////////////

        #region Private Static Data
        private static int resetCount;
        private static int timeoutCount;
        #endregion

        ///////////////////////////////////////////////////////////////////////

        #region Private Static Methods
        private static void DoTransactions(SQLiteConnection connection)
        {
          Random random = new Random();

          for (int iteration = 0; iteration < 10000; iteration++)
          {
            using (TransactionScope transactionScope = new TransactionScope())
            {
              EnlistmentNotification.UseDistributedTransaction(false);

              TransactionInformation transactionInformation =
                  Transaction.Current.TransactionInformation;

              if (transactionInformation.DistributedIdentifier.Equals(
                  Guid.Empty))
              {
                throw new Exception("distributed identifier is empty");
              }

              connection.EnlistTransaction(Transaction.Current);

              using (SQLiteCommand command = connection.CreateCommand())
              {
                command.CommandText = "INSERT INTO t1(x) VALUES(?);";
                command.Parameters.Add(new SQLiteParameter("", iteration));
                command.ExecuteNonQuery();
              }

              transactionScope.Complete();
            }

            Thread.Sleep(random.Next(10));
          }
        }

        ///////////////////////////////////////////////////////////////////////

        private static void WaitOnEnlistments(object state)
        {
          SQLiteConnection connection = (SQLiteConnection)state;
          Random random = new Random();

          for (int iteration = 0; iteration < 1000; iteration++)
          {
            if (connection.WaitForEnlistmentReset(1))
              Interlocked.Increment(ref resetCount);
            else
              Interlocked.Increment(ref timeoutCount);

            Thread.Sleep(random.Next(100));
          }
        }
        #endregion

        ///////////////////////////////////////////////////////////////////////

        #region Public Static Methods
        public static string DoTest()
        {
          using (SQLiteConnection connection = new SQLiteConnection(
              "Data Source=${dataSource};[getTestProperties]"))
          {
            ThreadPool.QueueUserWorkItem(WaitOnEnlistments, connection);

            connection.Open();

            DoTransactions(connection);
          }

          int count1 = Interlocked.CompareExchange(ref resetCount, 0, 0);
          int count2 = Interlocked.CompareExchange(ref timeoutCount, 0, 0);

          return String.Format("{0}, {1}", count1, count2);
        }

        ///////////////////////////////////////////////////////////////////////

        public static void Main()
        {
          // do nothing.
        }
        #endregion
      }
    }
  }] 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} DoTest
      } result] : [set result ""]}] \
      [expr {[lindex $result 0] > 0}] \
      [expr {[lindex $result 1] > 0}]
} -cleanup {
  cleanupDb $fileName

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

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

runSQLiteTestEpilogue
runTestEpilogue

Changes to readme.htm.

209
210
211
212
213
214
215


216
217
218
219
220
221
222

<p>
    <b>1.0.107.0 - January XX, 2018 <font color="red">(release scheduled)</font></b>
</p>
<ul>
    <li>Updated to <a href="https://www.sqlite.org/draft/releaselog/3_22_0.html">SQLite 3.22.0</a>.</li>
    <li>Improve performance of type name lookups by removing superfluous locking and string creation.</li>


    <li>Fix some internal memory accounting present only in the debug build.</li>
    <li>Make sure inbound native delegates are unhooked before adding a connection to the pool. Fix for [0e48e80333].</li>
    <li>Add preliminary support for the .NET Framework 4.7.1.</li>
    <li>Updates to internal DbType mapping related lookup tables. Pursuant to [a799e3978f].</li>
</ul>
<p>
    <b>1.0.106.0 - November 2, 2017</b>







>
>







209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224

<p>
    <b>1.0.107.0 - January XX, 2018 <font color="red">(release scheduled)</font></b>
</p>
<ul>
    <li>Updated to <a href="https://www.sqlite.org/draft/releaselog/3_22_0.html">SQLite 3.22.0</a>.</li>
    <li>Improve performance of type name lookups by removing superfluous locking and string creation.</li>
    <li>Support asynchronous completion of distributed transactions. Fix for [5cee5409f8].</li>
    <li>Add experimental WaitForEnlistmentReset method to the SQLiteConnection class. Pursuant to [7e1dd697dc].</li>
    <li>Fix some internal memory accounting present only in the debug build.</li>
    <li>Make sure inbound native delegates are unhooked before adding a connection to the pool. Fix for [0e48e80333].</li>
    <li>Add preliminary support for the .NET Framework 4.7.1.</li>
    <li>Updates to internal DbType mapping related lookup tables. Pursuant to [a799e3978f].</li>
</ul>
<p>
    <b>1.0.106.0 - November 2, 2017</b>

Changes to www/news.wiki.

46
47
48
49
50
51
52


53
54
55
56
57
58
59

<p>
    <b>1.0.107.0 - January XX, 2018 <font color="red">(release scheduled)</font></b>
</p>
<ul>
    <li>Updated to [https://www.sqlite.org/draft/releaselog/3_22_0.html|SQLite 3.22.0].</li>
    <li>Improve performance of type name lookups by removing superfluous locking and string creation.</li>


    <li>Fix some internal memory accounting present only in the debug build.</li>
    <li>Make sure inbound native delegates are unhooked before adding a connection to the pool. Fix for [0e48e80333].</li>
    <li>Add preliminary support for the .NET Framework 4.7.1.</li>
    <li>Updates to internal DbType mapping related lookup tables. Pursuant to [a799e3978f].</li>
</ul>
<p>
    <b>1.0.106.0 - November 2, 2017</b>







>
>







46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61

<p>
    <b>1.0.107.0 - January XX, 2018 <font color="red">(release scheduled)</font></b>
</p>
<ul>
    <li>Updated to [https://www.sqlite.org/draft/releaselog/3_22_0.html|SQLite 3.22.0].</li>
    <li>Improve performance of type name lookups by removing superfluous locking and string creation.</li>
    <li>Support asynchronous completion of distributed transactions. Fix for [5cee5409f8].</li>
    <li>Add experimental WaitForEnlistmentReset method to the SQLiteConnection class. Pursuant to [7e1dd697dc].</li>
    <li>Fix some internal memory accounting present only in the debug build.</li>
    <li>Make sure inbound native delegates are unhooked before adding a connection to the pool. Fix for [0e48e80333].</li>
    <li>Add preliminary support for the .NET Framework 4.7.1.</li>
    <li>Updates to internal DbType mapping related lookup tables. Pursuant to [a799e3978f].</li>
</ul>
<p>
    <b>1.0.106.0 - November 2, 2017</b>