/ Check-in [4464ca1d]
Login

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

Overview
Comment:Use a blocking call to obtain the wal-mode WRITER lock in some cases.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | server-edition
Files: files | file ages | folders
SHA3-256: 4464ca1d686b5c457995cc885d4a8704e402ad387aa4cc37de199276b28cc08e
User & Date: dan 2017-05-10 13:46:29
Context
2017-05-10
16:18
Fix a problem causing a lock to be held past the end of a transaction. Use a blocking lock to take the read-lock on page 1 taken by all transactions. check-in: 2584df3d user: dan tags: server-edition
13:46
Use a blocking call to obtain the wal-mode WRITER lock in some cases. check-in: 4464ca1d user: dan tags: server-edition
2017-05-09
16:32
Fix a problem with wrapping the log file in server mode. check-in: 270b7d1e user: dan tags: server-edition
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/pager.c.

5684
5685
5686
5687
5688
5689
5690
5691
5692
5693
5694
5695
5696
5697
5698
....
5992
5993
5994
5995
5996
5997
5998
5999
6000
6001
6002
6003
6004
6005
6006
....
7665
7666
7667
7668
7669
7670
7671
7672
7673
7674
7675
7676
  Pager *pPager,      /* The pager open on the database file */
  Pgno pgno,          /* Page number to fetch */
  DbPage **ppPage,    /* Write a pointer to the page here */
  int flags           /* PAGER_GET_XXX flags */
){
#ifdef SQLITE_SERVER_EDITION
  if( pagerIsServer(pPager) ){
    int rc = sqlite3ServerLock(pPager->pServer, pgno, 0);
    if( rc!=SQLITE_OK ) return rc;
  }
#endif
  return pPager->xGet(pPager, pgno, ppPage, flags);
}

/*
................................................................................
  assert( assert_pager_state(pPager) );
  assert( pPager->errCode==0 );
  assert( pPager->readOnly==0 );
  CHECK_PAGE(pPg);

#ifdef SQLITE_SERVER_EDITION
  if( pagerIsServer(pPager) ){
    rc = sqlite3ServerLock(pPager->pServer, pPg->pgno, 1);
    if( rc!=SQLITE_OK ) return rc;
  }
#endif

  /* The journal file needs to be opened. Higher level routines have already
  ** obtained the necessary locks to begin the write-transaction, but the
  ** rollback journal might not yet be open. Open it now if this is the case.
................................................................................
#endif

#ifdef SQLITE_SERVER_EDITION
int sqlite3PagerIsServer(Pager *pPager){
  return pagerIsServer(pPager);
}
int sqlite3PagerPagelock(Pager *pPager, Pgno pgno, int bWrite){
  return sqlite3ServerLock(pPager->pServer, pgno, bWrite);
}
#endif

#endif /* SQLITE_OMIT_DISKIO */







|







 







|







 







|




5684
5685
5686
5687
5688
5689
5690
5691
5692
5693
5694
5695
5696
5697
5698
....
5992
5993
5994
5995
5996
5997
5998
5999
6000
6001
6002
6003
6004
6005
6006
....
7665
7666
7667
7668
7669
7670
7671
7672
7673
7674
7675
7676
  Pager *pPager,      /* The pager open on the database file */
  Pgno pgno,          /* Page number to fetch */
  DbPage **ppPage,    /* Write a pointer to the page here */
  int flags           /* PAGER_GET_XXX flags */
){
#ifdef SQLITE_SERVER_EDITION
  if( pagerIsServer(pPager) ){
    int rc = sqlite3ServerLock(pPager->pServer, pgno, 0, 0);
    if( rc!=SQLITE_OK ) return rc;
  }
#endif
  return pPager->xGet(pPager, pgno, ppPage, flags);
}

/*
................................................................................
  assert( assert_pager_state(pPager) );
  assert( pPager->errCode==0 );
  assert( pPager->readOnly==0 );
  CHECK_PAGE(pPg);

#ifdef SQLITE_SERVER_EDITION
  if( pagerIsServer(pPager) ){
    rc = sqlite3ServerLock(pPager->pServer, pPg->pgno, 1, 0);
    if( rc!=SQLITE_OK ) return rc;
  }
#endif

  /* The journal file needs to be opened. Higher level routines have already
  ** obtained the necessary locks to begin the write-transaction, but the
  ** rollback journal might not yet be open. Open it now if this is the case.
................................................................................
#endif

#ifdef SQLITE_SERVER_EDITION
int sqlite3PagerIsServer(Pager *pPager){
  return pagerIsServer(pPager);
}
int sqlite3PagerPagelock(Pager *pPager, Pgno pgno, int bWrite){
  return sqlite3ServerLock(pPager->pServer, pgno, bWrite, 0);
}
#endif

#endif /* SQLITE_OMIT_DISKIO */

Changes to src/server.c.

352
353
354
355
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
...
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
...
432
433
434
435
436
437
438



439
440
441
442
443
444
445
446
447
...
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
    sqlite3ServerDisconnect(p, 0);
    p = 0;
  }
  *ppOut = p;
  return rc;
}

static int serverOvercomeLock(Server *p, int bWrite, u32 v, int *pbRetry){






  int rc = SQLITE_OK;
  int bLocal = 0;
  int iBlock = ((int)(v>>HMA_CLIENT_SLOTS))-1;

  if( iBlock<0 ){
    for(iBlock=0; iBlock<HMA_CLIENT_SLOTS; iBlock++){
      if( iBlock!=p->iClient && (v & (1<<iBlock)) ) break;
    }
  }
  assert( iBlock<HMA_CLIENT_SLOTS );

  serverEnterMutex();

  if( p->pHma->aClient[iBlock] ){
    bLocal = 1;
  }else{
    rc = posixLock(p->pHma->fd, iBlock+1, SERVER_WRITE_LOCK, 0);
  }

  if( bLocal==0 && rc==SQLITE_OK ){
    rc = serverRollbackClient(p, iBlock);

    /* Release the lock on slot iBlock */
    posixLock(p->pHma->fd, iBlock+1, SERVER_NO_LOCK, 0);
    if( rc==SQLITE_OK ){
      *pbRetry = 1;
    }
  }else{









    assert( rc==SQLITE_OK || rc==SQLITE_BUSY );
    rc = SQLITE_OK;
  }



  serverLeaveMutex();

  return rc;
}

/*
** Begin a transaction.
*/
int sqlite3ServerBegin(Server *p){
#if 0
  return posixLock(p->pHma->fd, p->iClient+1, SERVER_WRITE_LOCK, 0);

#endif
  return sqlite3ServerLock(p, 1, 0);
}

/*
** End a transaction (and release all locks).
*/
int sqlite3ServerEnd(Server *p){
  int i;
................................................................................
        n = n & ((1 << HMA_CLIENT_SLOTS)-1);
      }
      n = n & ~(1 << p->iClient);
      if( __sync_val_compare_and_swap(pSlot, v, n)==v ) break;
    }
  }
  p->nLock = 0;
#if 0
  return posixLock(p->pHma->fd, p->iClient+1, SERVER_READ_LOCK, 0);
#endif
  return SQLITE_OK;
}

/*
** Release all write-locks.
................................................................................
int sqlite3ServerReleaseWriteLocks(Server *p){
  int rc = SQLITE_OK;
  return rc;
}

/*
** Lock page pgno for reading (bWrite==0) or writing (bWrite==1).



*/
int sqlite3ServerLock(Server *p, Pgno pgno, int bWrite){
  int rc = SQLITE_OK;

  /* Grow the aLock[] array, if required */
  if( p->nLock==p->nAlloc ){
    int nNew = p->nAlloc ? p->nAlloc*2 : 128;
    u32 *aNew;
    aNew = (u32*)sqlite3_realloc(p->aLock, sizeof(u32)*nNew);
................................................................................
    }

    while( 1 ){
      u32 n;

      while( (bWrite && (v & ~(1 << p->iClient))) || (v >> HMA_CLIENT_SLOTS) ){
        int bRetry = 0;
        rc = serverOvercomeLock(p, bWrite, v, &bRetry);
        if( rc!=SQLITE_OK ) goto server_lock_out;
        if( bRetry==0 ){
          /* There is a conflicting lock. Cannot obtain this lock. */
          sqlite3_log(SQLITE_BUSY_DEADLOCK, "Conflict at page %d", (int)pgno);
          rc = SQLITE_BUSY_DEADLOCK;
          goto server_lock_out;
        }







|
>
>
>
>
>
>

<










>
|
<
<

<
<
|
|

|
|
|
|
|
<
>
>
>
>
>
>
>
>
>
|
|
|
>
>
>









|
|
>

|







 







|







 







>
>
>

|







 







|







352
353
354
355
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
...
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
...
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
...
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
    sqlite3ServerDisconnect(p, 0);
    p = 0;
  }
  *ppOut = p;
  return rc;
}

static int serverOvercomeLock(
  Server *p,                      /* Server connection */
  int bWrite,                     /* True for a write-lock */
  int bBlock,                     /* If true, block for this lock */
  u32 v,                          /* Value of blocking page locking slot */
  int *pbRetry                    /* OUT: True if caller should retry lock */
){
  int rc = SQLITE_OK;

  int iBlock = ((int)(v>>HMA_CLIENT_SLOTS))-1;

  if( iBlock<0 ){
    for(iBlock=0; iBlock<HMA_CLIENT_SLOTS; iBlock++){
      if( iBlock!=p->iClient && (v & (1<<iBlock)) ) break;
    }
  }
  assert( iBlock<HMA_CLIENT_SLOTS );

  serverEnterMutex();

  if( 0==p->pHma->aClient[iBlock] ){


    rc = posixLock(p->pHma->fd, iBlock+1, SERVER_WRITE_LOCK, 0);


    if( rc==SQLITE_OK ){
      rc = serverRollbackClient(p, iBlock);

      /* Release the lock on slot iBlock */
      posixLock(p->pHma->fd, iBlock+1, SERVER_NO_LOCK, 0);
      if( rc==SQLITE_OK ){
        *pbRetry = 1;
      }

    }else if( rc==SQLITE_BUSY ){
      if( bBlock ){
        rc = posixLock(p->pHma->fd, iBlock+1, SERVER_READ_LOCK, 1);
        if( rc==SQLITE_OK ){
          posixLock(p->pHma->fd, iBlock+1, SERVER_NO_LOCK, 0);
          *pbRetry = 1;
        }
      }

      if( rc==SQLITE_BUSY ){
        rc = SQLITE_OK;
      }
    }
  }

  serverLeaveMutex();

  return rc;
}

/*
** Begin a transaction.
*/
int sqlite3ServerBegin(Server *p){
#if 1
  int rc = posixLock(p->pHma->fd, p->iClient+1, SERVER_WRITE_LOCK, 1);
  if( rc ) return rc;
#endif
  return sqlite3ServerLock(p, 1, 0, 0);
}

/*
** End a transaction (and release all locks).
*/
int sqlite3ServerEnd(Server *p){
  int i;
................................................................................
        n = n & ((1 << HMA_CLIENT_SLOTS)-1);
      }
      n = n & ~(1 << p->iClient);
      if( __sync_val_compare_and_swap(pSlot, v, n)==v ) break;
    }
  }
  p->nLock = 0;
#if 1
  return posixLock(p->pHma->fd, p->iClient+1, SERVER_READ_LOCK, 0);
#endif
  return SQLITE_OK;
}

/*
** Release all write-locks.
................................................................................
int sqlite3ServerReleaseWriteLocks(Server *p){
  int rc = SQLITE_OK;
  return rc;
}

/*
** Lock page pgno for reading (bWrite==0) or writing (bWrite==1).
**
** If parameter bBlock is non-zero, then make this a blocking lock if
** possible.
*/
int sqlite3ServerLock(Server *p, Pgno pgno, int bWrite, int bBlock){
  int rc = SQLITE_OK;

  /* Grow the aLock[] array, if required */
  if( p->nLock==p->nAlloc ){
    int nNew = p->nAlloc ? p->nAlloc*2 : 128;
    u32 *aNew;
    aNew = (u32*)sqlite3_realloc(p->aLock, sizeof(u32)*nNew);
................................................................................
    }

    while( 1 ){
      u32 n;

      while( (bWrite && (v & ~(1 << p->iClient))) || (v >> HMA_CLIENT_SLOTS) ){
        int bRetry = 0;
        rc = serverOvercomeLock(p, bWrite, bBlock, v, &bRetry);
        if( rc!=SQLITE_OK ) goto server_lock_out;
        if( bRetry==0 ){
          /* There is a conflicting lock. Cannot obtain this lock. */
          sqlite3_log(SQLITE_BUSY_DEADLOCK, "Conflict at page %d", (int)pgno);
          rc = SQLITE_BUSY_DEADLOCK;
          goto server_lock_out;
        }

Changes to src/wal.c.

3090
3091
3092
3093
3094
3095
3096

3097


3098
3099
3100
3101
3102
3103
3104
....
3371
3372
3373
3374
3375
3376
3377



3378

3379
3380
3381
3382
3383
3384
3385
  u32 iFirst = 0;                 /* First frame that may be overwritten */
  WalIndexHdr *pLive;             /* Pointer to shared header */

  assert( pList );
  assert( pWal->writeLock || walIsServer(pWal) );
  if( pWal->writeLock==0 ){
    int bDummy = 0;

    rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1);


    if( rc==SQLITE_OK ){
      pWal->writeLock = 1;
      rc = walIndexTryHdr(pWal, &bDummy);
    }
    if( rc!=SQLITE_OK ){
      return rc;
    }
................................................................................
  **
  ** EVIDENCE-OF: R-60642-04082 If the writer lock cannot be obtained
  ** immediately, and a busy-handler is configured, it is invoked and the
  ** writer lock retried until either the busy-handler returns 0 or the
  ** lock is successfully obtained.
  */
  if( eMode!=SQLITE_CHECKPOINT_PASSIVE ){



    rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_WRITE_LOCK, 1);

    if( rc==SQLITE_OK ){
      pWal->writeLock = 1;
    }else if( rc==SQLITE_BUSY ){
      eMode2 = SQLITE_CHECKPOINT_PASSIVE;
      xBusy2 = 0;
      rc = SQLITE_OK;
    }







>

>
>







 







>
>
>
|
>







3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
....
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384
3385
3386
3387
3388
3389
3390
3391
3392
  u32 iFirst = 0;                 /* First frame that may be overwritten */
  WalIndexHdr *pLive;             /* Pointer to shared header */

  assert( pList );
  assert( pWal->writeLock || walIsServer(pWal) );
  if( pWal->writeLock==0 ){
    int bDummy = 0;
#if 0
    rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1);
#endif
    rc = sqlite3ServerLock(pWal->pServer, 0, 1, 1);
    if( rc==SQLITE_OK ){
      pWal->writeLock = 1;
      rc = walIndexTryHdr(pWal, &bDummy);
    }
    if( rc!=SQLITE_OK ){
      return rc;
    }
................................................................................
  **
  ** EVIDENCE-OF: R-60642-04082 If the writer lock cannot be obtained
  ** immediately, and a busy-handler is configured, it is invoked and the
  ** writer lock retried until either the busy-handler returns 0 or the
  ** lock is successfully obtained.
  */
  if( eMode!=SQLITE_CHECKPOINT_PASSIVE ){
    if( walIsServer(pWal) ){
      rc = sqlite3ServerLock(pWal->pServer, 0, 1, 1);
    }else{
      rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_WRITE_LOCK, 1);
    }
    if( rc==SQLITE_OK ){
      pWal->writeLock = 1;
    }else if( rc==SQLITE_BUSY ){
      eMode2 = SQLITE_CHECKPOINT_PASSIVE;
      xBusy2 = 0;
      rc = SQLITE_OK;
    }