/ Check-in [b8196056]
Login
SQLite training in Houston TX on 2019-11-05 (details)
Part of the 2019 Tcl Conference

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

Overview
Comment:Avoid a race condition that might cause a busy_timeout to last longer than it should.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | lowlevel-lock-timeout
Files: files | file ages | folders
SHA3-256: b81960561b47a1b49646f2f8870dd0684dc4ca7c0b9e11076fd713de66b75972
User & Date: drh 2018-03-26 20:43:05
Context
2018-03-26
21:05
Do not inject OOM errors on SQLITE_FCNTL_LOCK_TIMEOUT calls as an OOM is not possible in that context. Closed-Leaf check-in: 5474e560 user: drh tags: lowlevel-lock-timeout
20:43
Avoid a race condition that might cause a busy_timeout to last longer than it should. check-in: b8196056 user: drh tags: lowlevel-lock-timeout
17:40
Add infrastructure to support for using F_SETLKW with a timeout on system that support that functionality. Requires SQLITE_ENABLE_SETLK_TIMEOUT. check-in: 2e54a743 user: drh tags: lowlevel-lock-timeout
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/btree.c.

3369
3370
3371
3372
3373
3374
3375

3376
3377
3378
3379
3380
3381
3382
    }
  
    if( rc!=SQLITE_OK ){
      unlockBtreeIfUnused(pBt);
    }
  }while( (rc&0xFF)==SQLITE_BUSY && pBt->inTransaction==TRANS_NONE &&
          btreeInvokeBusyHandler(pBt) );


  if( rc==SQLITE_OK ){
    if( p->inTrans==TRANS_NONE ){
      pBt->nTransaction++;
#ifndef SQLITE_OMIT_SHARED_CACHE
      if( p->sharable ){
        assert( p->lock.pBtree==p && p->lock.iTable==1 );







>







3369
3370
3371
3372
3373
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
    }
  
    if( rc!=SQLITE_OK ){
      unlockBtreeIfUnused(pBt);
    }
  }while( (rc&0xFF)==SQLITE_BUSY && pBt->inTransaction==TRANS_NONE &&
          btreeInvokeBusyHandler(pBt) );
  sqlite3PagerResetLockTimeout(pBt->pPager);

  if( rc==SQLITE_OK ){
    if( p->inTrans==TRANS_NONE ){
      pBt->nTransaction++;
#ifndef SQLITE_OMIT_SHARED_CACHE
      if( p->sharable ){
        assert( p->lock.pBtree==p && p->lock.iTable==1 );

Changes to src/os_unix.c.

1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
#else
static int osSetPosixAdvisoryLock(
  int h,                /* The file descriptor on which to take the lock */
  struct flock *pLock,  /* The description of the lock */
  unixFile *pFile       /* Structure holding timeout value */
){
  int rc = osFcntl(h,F_SETLK,pLock);
  if( rc<0 && pFile->iBusyTimeout>0 ){
    /* On systems that support some kind of blocking file lock with a timeout,
    ** make appropriate changes here to invoke that blocking file lock.  On
    ** generic posix, however, there is no such API.  So we simply try the
    ** lock once every millisecond until either the timeout expires, or until
    ** the lock is obtained. */
    do{
      usleep(1000);
      rc = osFcntl(h,F_SETLK,pLock);
      pFile->iBusyTimeout--;
    }while( rc<0 && pFile->iBusyTimeout>0 );
    pFile->iBusyTimeout = 0;
  }
  return rc;
}
#endif /* SQLITE_ENABLE_SETLK_TIMEOUT */


/*







|





<
|
|
|
<
<







1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498

1499
1500
1501


1502
1503
1504
1505
1506
1507
1508
#else
static int osSetPosixAdvisoryLock(
  int h,                /* The file descriptor on which to take the lock */
  struct flock *pLock,  /* The description of the lock */
  unixFile *pFile       /* Structure holding timeout value */
){
  int rc = osFcntl(h,F_SETLK,pLock);
  while( rc<0 && pFile->iBusyTimeout>0 ){
    /* On systems that support some kind of blocking file lock with a timeout,
    ** make appropriate changes here to invoke that blocking file lock.  On
    ** generic posix, however, there is no such API.  So we simply try the
    ** lock once every millisecond until either the timeout expires, or until
    ** the lock is obtained. */

    usleep(1000);
    rc = osFcntl(h,F_SETLK,pLock);
    pFile->iBusyTimeout--;


  }
  return rc;
}
#endif /* SQLITE_ENABLE_SETLK_TIMEOUT */


/*

Changes to src/pager.c.

5689
5690
5691
5692
5693
5694
5695

5696
5697
5698
5699
5700
5701
5702
....
6966
6967
6968
6969
6970
6971
6972












6973
6974
6975
6976
6977
6978
6979
....
7426
7427
7428
7429
7430
7431
7432

7433
7434
7435
7436
7437
7438
7439
}
void sqlite3PagerUnrefPageOne(DbPage *pPg){
  Pager *pPager;
  assert( pPg!=0 );
  assert( pPg->pgno==1 );
  assert( (pPg->flags & PGHDR_MMAP)==0 ); /* Page1 is never memory mapped */
  pPager = pPg->pPager;

  sqlite3PcacheRelease(pPg);
  pagerUnlockIfUnused(pPager);
}

/*
** This function is called at the start of every write transaction.
** There must already be a RESERVED or EXCLUSIVE lock on the database 
................................................................................
** with the pager.  This might return NULL if the file has
** not yet been opened.
*/
sqlite3_file *sqlite3PagerFile(Pager *pPager){
  return pPager->fd;
}













/*
** Return the file handle for the journal file (if it exists).
** This will be either the rollback journal or the WAL file.
*/
sqlite3_file *sqlite3PagerJrnlFile(Pager *pPager){
#if SQLITE_OMIT_WAL
  return pPager->jfd;
................................................................................
  if( pPager->pWal ){
    rc = sqlite3WalCheckpoint(pPager->pWal, db, eMode,
        (eMode==SQLITE_CHECKPOINT_PASSIVE ? 0 : pPager->xBusyHandler),
        pPager->pBusyHandlerArg,
        pPager->walSyncFlags, pPager->pageSize, (u8 *)pPager->pTmpSpace,
        pnLog, pnCkpt
    );

  }
  return rc;
}

int sqlite3PagerWalCallback(Pager *pPager){
  return sqlite3WalCallback(pPager->pWal);
}







>







 







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







 







>







5689
5690
5691
5692
5693
5694
5695
5696
5697
5698
5699
5700
5701
5702
5703
....
6967
6968
6969
6970
6971
6972
6973
6974
6975
6976
6977
6978
6979
6980
6981
6982
6983
6984
6985
6986
6987
6988
6989
6990
6991
6992
....
7439
7440
7441
7442
7443
7444
7445
7446
7447
7448
7449
7450
7451
7452
7453
}
void sqlite3PagerUnrefPageOne(DbPage *pPg){
  Pager *pPager;
  assert( pPg!=0 );
  assert( pPg->pgno==1 );
  assert( (pPg->flags & PGHDR_MMAP)==0 ); /* Page1 is never memory mapped */
  pPager = pPg->pPager;
  sqlite3PagerResetLockTimeout(pPager);
  sqlite3PcacheRelease(pPg);
  pagerUnlockIfUnused(pPager);
}

/*
** This function is called at the start of every write transaction.
** There must already be a RESERVED or EXCLUSIVE lock on the database 
................................................................................
** with the pager.  This might return NULL if the file has
** not yet been opened.
*/
sqlite3_file *sqlite3PagerFile(Pager *pPager){
  return pPager->fd;
}

#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
/*
** Reset the lock timeout for pager.
*/
void sqlite3PagerResetLockTimeout(Pager *pPager){
  if( isOpen(pPager->fd) ){
    int x = 0;
    sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_LOCK_TIMEOUT, &x);
  }
}
#endif

/*
** Return the file handle for the journal file (if it exists).
** This will be either the rollback journal or the WAL file.
*/
sqlite3_file *sqlite3PagerJrnlFile(Pager *pPager){
#if SQLITE_OMIT_WAL
  return pPager->jfd;
................................................................................
  if( pPager->pWal ){
    rc = sqlite3WalCheckpoint(pPager->pWal, db, eMode,
        (eMode==SQLITE_CHECKPOINT_PASSIVE ? 0 : pPager->xBusyHandler),
        pPager->pBusyHandlerArg,
        pPager->walSyncFlags, pPager->pageSize, (u8 *)pPager->pTmpSpace,
        pnLog, pnCkpt
    );
    sqlite3PagerResetLockTimeout(pPager);
  }
  return rc;
}

int sqlite3PagerWalCallback(Pager *pPager){
  return sqlite3WalCallback(pPager->pWal);
}

Changes to src/pager.h.

208
209
210
211
212
213
214





215
216
217
218
219
220
221
sqlite3_file *sqlite3PagerJrnlFile(Pager*);
const char *sqlite3PagerJournalname(Pager*);
void *sqlite3PagerTempSpace(Pager*);
int sqlite3PagerIsMemdb(Pager*);
void sqlite3PagerCacheStat(Pager *, int, int, int *);
void sqlite3PagerClearCache(Pager*);
int sqlite3SectorSize(sqlite3_file *);






/* Functions used to truncate the database file. */
void sqlite3PagerTruncateImage(Pager*,Pgno);

void sqlite3PagerRekey(DbPage*, Pgno, u16);

#if defined(SQLITE_HAS_CODEC) && !defined(SQLITE_OMIT_WAL)







>
>
>
>
>







208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
sqlite3_file *sqlite3PagerJrnlFile(Pager*);
const char *sqlite3PagerJournalname(Pager*);
void *sqlite3PagerTempSpace(Pager*);
int sqlite3PagerIsMemdb(Pager*);
void sqlite3PagerCacheStat(Pager *, int, int, int *);
void sqlite3PagerClearCache(Pager*);
int sqlite3SectorSize(sqlite3_file *);
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
void sqlite3PagerResetLockTimeout(Pager *pPager);
#else
# define sqlite3PagerResetLockTimeout(X)
#endif

/* Functions used to truncate the database file. */
void sqlite3PagerTruncateImage(Pager*,Pgno);

void sqlite3PagerRekey(DbPage*, Pgno, u16);

#if defined(SQLITE_HAS_CODEC) && !defined(SQLITE_OMIT_WAL)