SQLite4
Check-in [58f7282211]
Not logged in

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

Overview
Comment:Btree fixes related to multiple client tests.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 58f72822114449bad7a89f89f8251f54852e050b
User & Date: dan 2013-10-30 19:57:25
Context
2013-10-31
16:31
Fix some problems causing multi-threaded btree tests to fail. Some still remain. check-in: 67b28147ea user: dan tags: trunk
2013-10-30
19:57
Btree fixes related to multiple client tests. check-in: 58f7282211 user: dan tags: trunk
18:37
Add sub-transactions to btree module. check-in: 9e00823074 user: dan tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to lsm-test/lsmtest_main.c.

61
62
63
64
65
66
67


68
69
70
71
72
73
74
  TestDb *pDb,                    /* Database handle */
  void *pKey, int nKey,           /* Key to query database for */
  void *pVal, int nVal,           /* Value to write */
  int *pRc                        /* IN/OUT: Error code */
){
  if( *pRc==0 ){
    int rc;


    rc = tdb_write(pDb, pKey, nKey, pVal, nVal);
    testSetError(rc);
  }
}
void testDelete(
  TestDb *pDb,                    /* Database handle */
  void *pKey, int nKey,           /* Key to query database for */







>
>







61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
  TestDb *pDb,                    /* Database handle */
  void *pKey, int nKey,           /* Key to query database for */
  void *pVal, int nVal,           /* Value to write */
  int *pRc                        /* IN/OUT: Error code */
){
  if( *pRc==0 ){
    int rc;
static int nCall = 0;
nCall++;
    rc = tdb_write(pDb, pKey, nKey, pVal, nVal);
    testSetError(rc);
  }
}
void testDelete(
  TestDb *pDb,                    /* Database handle */
  void *pKey, int nKey,           /* Key to query database for */

Changes to src/bt_lock.c.

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
...
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
...
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375




376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398





399
400






401
402
403
404
405
406


407
408
409
410
411
412
413

  assert( iLock>=0 && iLock<(BT_LOCK_READER0 + BT_NREADER) );
  assert( (BT_LOCK_READER0+BT_NREADER)<=32 );
  assert( eOp==BT_LOCK_UNLOCK || eOp==BT_LOCK_SHARED || eOp==BT_LOCK_EXCL );

  /* Check for a no-op. Proceed only if this is not one of those. */
  if( (eOp==BT_LOCK_UNLOCK && (mask & (p->mExclLock|p->mSharedLock))!=0)
   || (eOp==BT_LOCK_SHARED && (mask & (p->mExclLock|p->mSharedLock))==0)
   || (eOp==BT_LOCK_EXCL   && (mask & p->mExclLock)==0)
  ){
    BtLock *pIter;
    int nExcl = 0;                /* Number of connections holding EXCLUSIVE */
    int nShared = 0;              /* Number of connections holding SHARED */
    sqlite4_mutex_enter(pShared->pClientMutex);

    /* Figure out the locks currently held by this process on iLock, not
    ** including any held by this connection.  */
    for(pIter=pShared->pLock; pIter; pIter=pIter->pNext){
      assert( (pIter->mExclLock & pIter->mSharedLock)==pIter->mExclLock );
      if( pIter!=p ){
        assert( (pIter->mExclLock & p->mSharedLock)==0 );
        assert( (pIter->mSharedLock & p->mExclLock)==0 );
        if( mask & pIter->mExclLock ){
          nExcl++;
        }else if( mask & pIter->mSharedLock ){
          nShared++;
................................................................................
        if( nExcl || nShared ){
          rc = SQLITE4_BUSY;
        }else{
#if 0
          rc = lockSharedFile(db->pEnv, p, iLock, LSM_LOCK_EXCL);
#endif
          if( rc==SQLITE4_OK ){
            p->mSharedLock |= mask;
            p->mExclLock |= mask;
          }
        }
        break;
    }

    sqlite4_mutex_leave(p->pClientMutex);
................................................................................
  ** the entire log has been checkpointed (and iFirst is the "next" 
  ** frame to use). Handle this case in the same way as an empty 
  ** log file.  
  **
  ** It is also possible that the iFirst value (read from the shared-memory
  ** checkpoint header) is much newer than the aLog[] values (read from
  ** the snapshot header). If so, the caller will figure it out.  */
  if( iFirst<aLog[0] && iFirst>aLog[1] 
   && iFirst<aLog[2] && iFirst>aLog[3] 
   && iFirst<aLog[4] && iFirst>aLog[5] 
  ){
    iLast = 0;
  }

  if( iLast==0 ){
    rc = btLockLockop(pLock, BT_LOCK_READER_DBONLY, BT_LOCK_SHARED, 0);
  }else{
    int nAttempt = 100;           /* Remaining lock attempts */

    while( rc==SQLITE4_BUSY && (nAttempt--)>0 ){





      /* Try to find a slot populated with the values required. */
      for(i=0; i<BT_NREADER; i++){
        if( aSlot[i].iFirst==iFirst && aSlot[i].iLast==iLast ) break;
      }

      /* Or, if there is no slot with the required values - try to create one */
      if( i==BT_NREADER ){
        for(i=0; i<BT_NREADER; i++){
          rc = btLockLockop(pLock, BT_LOCK_READER0 + i, BT_LOCK_EXCL, 0);
          if( rc==SQLITE4_OK ){
            /* The EXCLUSIVE lock obtained by the successful call to
             ** btLockLockop() is released below by the call to obtain
             ** a SHARED lock on the same locking slot. */
            aSlot[i].iFirst = iFirst;
            aSlot[i].iLast = iLast;
            break;
          }else if( rc!=SQLITE4_BUSY ){
            return rc;
          }
        }
      }






      if( i==BT_NREADER ){
        /* TODO: Try to find a usable slot */






      }

      if( i<BT_NREADER ){
        rc = btLockLockop(pLock, BT_LOCK_READER0 + i, BT_LOCK_SHARED, 0);
        if( rc==SQLITE4_OK ){
          if( aSlot[i].iFirst!=iFirst || aSlot[i].iLast!=iLast ){


            btLockLockop(pLock, BT_LOCK_READER0 + i, BT_LOCK_UNLOCK, 0);
            rc = SQLITE4_BUSY;
          }
        }
      }
    }
  }







|










|







 







|







 







|
|
|










>
>
>
>












|
|









>
>
>
>
>

<
>
>
>
>
>
>





|
>
>







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
...
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
...
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408

409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429

  assert( iLock>=0 && iLock<(BT_LOCK_READER0 + BT_NREADER) );
  assert( (BT_LOCK_READER0+BT_NREADER)<=32 );
  assert( eOp==BT_LOCK_UNLOCK || eOp==BT_LOCK_SHARED || eOp==BT_LOCK_EXCL );

  /* Check for a no-op. Proceed only if this is not one of those. */
  if( (eOp==BT_LOCK_UNLOCK && (mask & (p->mExclLock|p->mSharedLock))!=0)
   || (eOp==BT_LOCK_SHARED && (mask & p->mSharedLock)==0)
   || (eOp==BT_LOCK_EXCL   && (mask & p->mExclLock)==0)
  ){
    BtLock *pIter;
    int nExcl = 0;                /* Number of connections holding EXCLUSIVE */
    int nShared = 0;              /* Number of connections holding SHARED */
    sqlite4_mutex_enter(pShared->pClientMutex);

    /* Figure out the locks currently held by this process on iLock, not
    ** including any held by this connection.  */
    for(pIter=pShared->pLock; pIter; pIter=pIter->pNext){
      assert( (pIter->mExclLock & pIter->mSharedLock)==0 );
      if( pIter!=p ){
        assert( (pIter->mExclLock & p->mSharedLock)==0 );
        assert( (pIter->mSharedLock & p->mExclLock)==0 );
        if( mask & pIter->mExclLock ){
          nExcl++;
        }else if( mask & pIter->mSharedLock ){
          nShared++;
................................................................................
        if( nExcl || nShared ){
          rc = SQLITE4_BUSY;
        }else{
#if 0
          rc = lockSharedFile(db->pEnv, p, iLock, LSM_LOCK_EXCL);
#endif
          if( rc==SQLITE4_OK ){
            p->mSharedLock &= ~mask;
            p->mExclLock |= mask;
          }
        }
        break;
    }

    sqlite4_mutex_leave(p->pClientMutex);
................................................................................
  ** the entire log has been checkpointed (and iFirst is the "next" 
  ** frame to use). Handle this case in the same way as an empty 
  ** log file.  
  **
  ** It is also possible that the iFirst value (read from the shared-memory
  ** checkpoint header) is much newer than the aLog[] values (read from
  ** the snapshot header). If so, the caller will figure it out.  */
  if( (iFirst<aLog[0] || iFirst>aLog[1])
   && (iFirst<aLog[2] || iFirst>aLog[3])
   && (iFirst<aLog[4] || iFirst>aLog[5])
  ){
    iLast = 0;
  }

  if( iLast==0 ){
    rc = btLockLockop(pLock, BT_LOCK_READER_DBONLY, BT_LOCK_SHARED, 0);
  }else{
    int nAttempt = 100;           /* Remaining lock attempts */

    while( rc==SQLITE4_BUSY && (nAttempt--)>0 ){
      int iIdxFirst = sqlite4BtLogFrameToIdx(aLog, iFirst);
      int iIdxLast = sqlite4BtLogFrameToIdx(aLog, iLast);

      assert( iIdxFirst>=0 && iIdxLast>=0 );

      /* Try to find a slot populated with the values required. */
      for(i=0; i<BT_NREADER; i++){
        if( aSlot[i].iFirst==iFirst && aSlot[i].iLast==iLast ) break;
      }

      /* Or, if there is no slot with the required values - try to create one */
      if( i==BT_NREADER ){
        for(i=0; i<BT_NREADER; i++){
          rc = btLockLockop(pLock, BT_LOCK_READER0 + i, BT_LOCK_EXCL, 0);
          if( rc==SQLITE4_OK ){
            /* The EXCLUSIVE lock obtained by the successful call to
            ** btLockLockop() is released below by the call to obtain
            ** a SHARED lock on the same locking slot. */
            aSlot[i].iFirst = iFirst;
            aSlot[i].iLast = iLast;
            break;
          }else if( rc!=SQLITE4_BUSY ){
            return rc;
          }
        }
      }

      /* If no existing slot with the required values was found, and the
      ** attempt to create one failed, search for any usable slot. A 
      ** usable slot is one where both the "iFirst" and "iLast" values
      ** occur at the same point or earlier in the log than the required
      ** iFirst/iLast values, respectively.  */
      if( i==BT_NREADER ){

        for(i=0; i<BT_NREADER; i++){
          int iSlotFirst = sqlite4BtLogFrameToIdx(aLog, aSlot[i].iFirst);
          int iSlotLast = sqlite4BtLogFrameToIdx(aLog, aSlot[i].iLast);
          if( iSlotFirst<0 || iSlotLast<0 ) continue;
          if( iSlotFirst<=iIdxFirst && iSlotLast<=iIdxLast ) break;
        }
      }

      if( i<BT_NREADER ){
        rc = btLockLockop(pLock, BT_LOCK_READER0 + i, BT_LOCK_SHARED, 0);
        if( rc==SQLITE4_OK ){
          int iSF = sqlite4BtLogFrameToIdx(aLog, aSlot[i].iFirst);
          int iSL = sqlite4BtLogFrameToIdx(aLog, aSlot[i].iLast);
          if( iSF>iIdxFirst || iSL>iIdxLast || iSF<0 || iSL<0 ){
            btLockLockop(pLock, BT_LOCK_READER0 + i, BT_LOCK_UNLOCK, 0);
            rc = SQLITE4_BUSY;
          }
        }
      }
    }
  }

Changes to src/bt_pager.c.

381
382
383
384
385
386
387

388
389
390
391
392
393
394

  rc = sqlite4BtLockDisconnect((BtLock*)p, btCheckpoint, btCleanup);

  if( p->btl.pFd ){
    p->btl.pVfs->xClose(p->btl.pFd);
  }


  btCloseSavepoints(p, 0);
  btPurgeCache(p);
  sqlite4BtLogClose(p->pLog, 0);
  sqlite4_free(p->btl.pEnv, p->zFile);
  sqlite4_free(p->btl.pEnv, p->aSavepoint);
  sqlite4_free(p->btl.pEnv, p);
  return rc;







>







381
382
383
384
385
386
387
388
389
390
391
392
393
394
395

  rc = sqlite4BtLockDisconnect((BtLock*)p, btCheckpoint, btCleanup);

  if( p->btl.pFd ){
    p->btl.pVfs->xClose(p->btl.pFd);
  }

  p->iTransactionLevel = 0;
  btCloseSavepoints(p, 0);
  btPurgeCache(p);
  sqlite4BtLogClose(p->pLog, 0);
  sqlite4_free(p->btl.pEnv, p->zFile);
  sqlite4_free(p->btl.pEnv, p->aSavepoint);
  sqlite4_free(p->btl.pEnv, p);
  return rc;