System.Data.SQLite

Login
This project makes use of Eagle, provided by Mistachkin Systems.
Eagle: Secure Software Automation

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

Overview
Comment:Add experimental WaitForEnlistmentReset method to the SQLiteConnection class.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | tkt-5cee5409f8
Files: files | file ages | folders
SHA1: 2d7e7593419da23c14068d681a39257f59bb1647
User & Date: mistachkin 2018-01-27 19:55:48
Context
2018-01-27
20:01
Minor revision to the previous check-in, check for the pathological case of time going backwards while sleeping. check-in: 2ac7dd7c98 user: mistachkin tags: tkt-5cee5409f8
19:55
Add experimental WaitForEnlistmentReset method to the SQLiteConnection class. check-in: 2d7e759341 user: mistachkin tags: tkt-5cee5409f8
2018-01-26
03:04
Fix infinite connection disposal cycle caused by not nulling out the connection enlistment for a rolled back transaction. check-in: 73c64a8452 user: mistachkin tags: tkt-5cee5409f8
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

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

14
15
16
17
18
19
20

21
22
23
24
25
26
27
  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" />,







>







14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
  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" />,
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
                // 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;







|







2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
                // 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;
3434
3435
3436
3437
3438
3439
3440












































































































3441
3442
3443
3444
3445
3446
3447
                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>
    /// <param name="key">The key to find</param>







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







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
                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
        )
    {
        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?
            //
            if (elapsed.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>
    /// <param name="key">The key to find</param>