System.Data.SQLite

Check-in [8f44046b6f]
Login

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

Overview
Comment:Synchronize access to the '_enlistment' field of SQLiteConnection objects. Even thought the SQLiteConnection class is not designed to be thread-safe, this field must be protected from multi-threaded access due to how (asynchronous) transaction scope completion is implemented by the .NET Framework. Candidate fix for ticket [5cee5409f8].
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | tkt-5cee5409f8
Files: files | file ages | folders
SHA1: 8f44046b6f67264564c507807abae51b2c9a7ab9
User & Date: mistachkin 2018-01-24 18:11:17.617
Original Comment: Synchronize access to the '_enlistment' field of SQLiteConnection objects. Even thought the SQLiteConnection class is not designed to be thread-safe, this field must be protected from multi-threaded access due to (asynchronous) transaction scope completion is implemented by the .NET Framework. Candidate fix for ticket [5cee5409f8].
Context
2018-01-25
18:53
In the enlistment cleanup, validate the 'cnn' parameter prior to attempting to use it. check-in: 6ec25f2c57 user: mistachkin tags: tkt-5cee5409f8
2018-01-24
18:11
Synchronize access to the '_enlistment' field of SQLiteConnection objects. Even thought the SQLiteConnection class is not designed to be thread-safe, this field must be protected from multi-threaded access due to how (asynchronous) transaction scope completion is implemented by the .NET Framework. Candidate fix for ticket [5cee5409f8]. check-in: 8f44046b6f user: mistachkin tags: tkt-5cee5409f8
2018-01-23
00:44
Update the Eagle shell configuration file. check-in: f722d13e2d user: mistachkin tags: trunk
Changes
Unified Diff Ignore Whitespace Patch
Changes to System.Data.SQLite/SQLiteConnection.cs.
1466
1467
1468
1469
1470
1471
1472






1473
1474
1475
1476
1477
1478
1479

    /// <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>







>
>
>
>
>
>







1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485

    /// <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>
2895
2896
2897
2898
2899
2900
2901


2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919

2920
2921
2922
2923
2924
2925
2926
      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();
          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;
        }







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

|
|

|
|
>







2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
      OnChanged(this, new ConnectionEventArgs(
          SQLiteConnectionEventType.Closing, null, null, null, null, null,
          null, null));

      if (_sql != null)
      {
#if !PLATFORM_COMPACTFRAMEWORK
        lock (_enlistmentSyncRoot) /* TRANSACTIONAL */
        {
          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();
            cnn._sql = _sql;
            cnn._transactionLevel = _transactionLevel;
            cnn._transactionSequence = _transactionSequence;
            cnn._enlistment = _enlistment;
            cnn._connectionState = _connectionState;
            cnn._version = _version;

            _enlistment._transaction._cnn = cnn;
            _enlistment._disposeConnection = true;

            _sql = null;
            _enlistment = null;
          }
        }
#endif
        if (_sql != null)
        {
          _sql.Close(_disposing);
          _sql = null;
        }
3363
3364
3365
3366
3367
3368
3369
3370
3371


3372
3373
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384
3385
3386
3387
3388
3389
3390
3391

3392
3393
3394
3395
3396
3397
3398
#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>







|

>
>
|
|
|
|

|
|
|
|

|
|

|
|
|

|
|
|
>







3372
3373
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384
3385
3386
3387
3388
3389
3390
3391
3392
3393
3394
3395
3396
3397
3398
3399
3400
3401
3402
3403
3404
3405
3406
3407
3408
3409
3410
#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 }));
        }
    }
#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.
183
184
185
186
187
188
189



190

191
192
193
194
195
196
197

    #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();








>
>
>
|
>







183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201

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

      SQLiteConnection cnn = _transaction.Connection;

      lock (cnn._enlistmentSyncRoot)
      {
          cnn._enlistment = null;
      }

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

226
227
228
229
230
231
232



233

234
235
236
237
238
239
240
    ///////////////////////////////////////////////////////////////////////////

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

      SQLiteConnection cnn = _transaction.Connection;



      cnn._enlistment = null;


      try
      {
        _transaction.Rollback();
        enlistment.Done();
      }
      finally







>
>
>
|
>







230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
    ///////////////////////////////////////////////////////////////////////////

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

      SQLiteConnection cnn = _transaction.Connection;

      lock (cnn._enlistmentSyncRoot)
      {
          cnn._enlistment = null;
      }

      try
      {
        _transaction.Rollback();
        enlistment.Done();
      }
      finally