aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/btree.c15
-rw-r--r--src/os_unix.c18
-rw-r--r--src/pager.c16
-rw-r--r--src/pager.h6
-rw-r--r--src/wal.c114
-rw-r--r--src/wal.h4
6 files changed, 126 insertions, 47 deletions
diff --git a/src/btree.c b/src/btree.c
index 76845e072..3b8d0529c 100644
--- a/src/btree.c
+++ b/src/btree.c
@@ -3437,6 +3437,18 @@ int sqlite3BtreeBeginTrans(Btree *p, int wrflag, int *pSchemaVersion){
pBt->btsFlags &= ~BTS_INITIALLY_EMPTY;
if( pBt->nPage==0 ) pBt->btsFlags |= BTS_INITIALLY_EMPTY;
do {
+ Pager *pPager = pBt->pPager;
+
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ /* If transitioning from no transaction directly to a write transaction,
+ ** block for the WRITER lock first if possible. */
+ if( pBt->pPage1==0 && wrflag ){
+ assert( pBt->inTransaction==TRANS_NONE );
+ rc = sqlite3PagerWalWriteLock(p->db, pPager, 1);
+ if( rc!=SQLITE_OK ) break;
+ }
+#endif
+
/* Call lockBtree() until either pBt->pPage1 is populated or
** lockBtree() returns something other than SQLITE_OK. lockBtree()
** may return SQLITE_OK but leave pBt->pPage1 set to 0 if after
@@ -3450,7 +3462,7 @@ int sqlite3BtreeBeginTrans(Btree *p, int wrflag, int *pSchemaVersion){
if( (pBt->btsFlags & BTS_READ_ONLY)!=0 ){
rc = SQLITE_READONLY;
}else{
- rc = sqlite3PagerBegin(pBt->pPager,wrflag>1,sqlite3TempInMemory(p->db));
+ rc = sqlite3PagerBegin(pPager, wrflag>1, sqlite3TempInMemory(p->db));
if( rc==SQLITE_OK ){
rc = newDatabase(pBt);
}else if( rc==SQLITE_BUSY_SNAPSHOT && pBt->inTransaction==TRANS_NONE ){
@@ -3463,6 +3475,7 @@ int sqlite3BtreeBeginTrans(Btree *p, int wrflag, int *pSchemaVersion){
}
if( rc!=SQLITE_OK ){
+ sqlite3PagerWalWriteLock(p->db, pPager, 0);
unlockBtreeIfUnused(pBt);
}
}while( (rc&0xFF)==SQLITE_BUSY && pBt->inTransaction==TRANS_NONE &&
diff --git a/src/os_unix.c b/src/os_unix.c
index 56e53929e..e1ed575b0 100644
--- a/src/os_unix.c
+++ b/src/os_unix.c
@@ -4819,22 +4819,24 @@ static int unixShmLock(
assert( pShmNode->hShm>=0 || pDbFd->pInode->bProcessLock==1 );
assert( pShmNode->hShm<0 || pDbFd->pInode->bProcessLock==0 );
- /* Check that, if this to be a blocking lock, that locks have been
- ** obtained in the following order.
+ /* Check that, if this to be a blocking lock, no locks that occur later
+ ** in the following list than the lock being obtained are already held:
**
** 1. Checkpointer lock (ofst==1).
- ** 2. Recover lock (ofst==2).
+ ** 2. Write lock (ofst==0).
** 3. Read locks (ofst>=3 && ofst<SQLITE_SHM_NLOCK).
- ** 4. Write lock (ofst==0).
**
** In other words, if this is a blocking lock, none of the locks that
** occur later in the above list than the lock being obtained may be
** held. */
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
- assert( pDbFd->iBusyTimeout==0
- || (flags & SQLITE_SHM_UNLOCK) || ofst==0
- || ((p->exclMask|p->sharedMask)&~((1<<ofst)-2))==0
- );
+ assert( (flags & SQLITE_SHM_UNLOCK) || pDbFd->iBusyTimeout==0 || (
+ (ofst!=2) /* not RECOVER */
+ && (n==1) /* Single lock only */
+ && (ofst!=1 || (p->exclMask|p->sharedMask)==0)
+ && (ofst!=0 || (p->exclMask|p->sharedMask)<3)
+ && (ofst<3 || (p->exclMask|p->sharedMask)<(1<<ofst))
+ ));
#endif
mask = (1<<(ofst+n)) - (1<<ofst);
diff --git a/src/pager.c b/src/pager.c
index dd723b788..1570b3e70 100644
--- a/src/pager.c
+++ b/src/pager.c
@@ -7574,7 +7574,21 @@ int sqlite3PagerCloseWal(Pager *pPager, sqlite3 *db){
return rc;
}
-
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+/*
+** If pager pPager is a wal-mode database not in exclusive locking mode,
+** invoke the sqlite3WalWriteLock() function on the associated Wal object
+** with the same db and bLock parameters as were passed to this function.
+** Return an SQLite error code if an error occurs, or SQLITE_OK otherwise.
+*/
+int sqlite3PagerWalWriteLock(sqlite3 *db, Pager *pPager, int bLock){
+ int rc = SQLITE_OK;
+ if( pagerUseWal(pPager) && pPager->exclusiveMode==0 ){
+ rc = sqlite3WalWriteLock(db, pPager->pWal, bLock);
+ }
+ return rc;
+}
+#endif
#ifdef SQLITE_ENABLE_SNAPSHOT
/*
diff --git a/src/pager.h b/src/pager.h
index a044daa18..c203908c8 100644
--- a/src/pager.h
+++ b/src/pager.h
@@ -185,6 +185,12 @@ int sqlite3PagerSharedLock(Pager *pPager);
# endif
#endif
+#if !defined(SQLITE_OMIT_WAL) && defined(SQLITE_ENABLE_SETLK_TIMEOUT)
+ int sqlite3PagerWalWriteLock(sqlite3*, Pager*, int);
+#else
+# define sqlite3PagerWalWriteLock(x,y,z) SQLITE_OK
+#endif
+
#ifdef SQLITE_DIRECT_OVERFLOW_READ
int sqlite3PagerDirectReadOk(Pager *pPager, Pgno pgno);
#endif
diff --git a/src/wal.c b/src/wal.c
index 020a481e2..a70a71624 100644
--- a/src/wal.c
+++ b/src/wal.c
@@ -2731,6 +2731,65 @@ int sqlite3WalSnapshotRecover(Wal *pWal){
}
#endif /* SQLITE_ENABLE_SNAPSHOT */
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+/*
+** Attempt to enable blocking locks. Blocking locks are enabled only if (a)
+** they are supported by the VFS, and (b) the database handle is configured
+** with a busy-timeout. Return 1 if blocking locks are successfully enabled,
+** or 0 otherwise.
+*/
+static int walEnableBlocking(sqlite3 *db, Wal *pWal){
+ int res = 0;
+ if( db->busyTimeout ){
+ int rc;
+ int tmout = db->busyTimeout;
+ rc = sqlite3OsFileControl(
+ pWal->pDbFd, SQLITE_FCNTL_LOCK_TIMEOUT, (void*)&tmout
+ );
+ res = (rc==SQLITE_OK);
+ }
+ return res;
+}
+
+/*
+** Disable blocking locks.
+*/
+static void walDisableBlocking(Wal *pWal){
+ int tmout = 0;
+ sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_LOCK_TIMEOUT, (void*)&tmout);
+}
+
+/*
+** If parameter bLock is true, attempt to enable blocking locks, take
+** the WRITER lock, and then disable blocking locks. If blocking locks
+** cannot be enabled, no attempt to obtain the WRITER lock is made. Return
+** an SQLite error code if an error occurs, or SQLITE_OK otherwise. It is not
+** an error if blocking locks can not be enabled.
+**
+** If the bLock parameter is false and the WRITER lock is held, release it.
+*/
+int sqlite3WalWriteLock(sqlite3 *db, Wal *pWal, int bLock){
+ int rc = SQLITE_OK;
+ assert( pWal->readLock<0 || bLock==0 );
+ if( bLock ){
+ if( walEnableBlocking(db, pWal) ){
+ rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1);
+ if( rc==SQLITE_OK ){
+ pWal->writeLock = 1;
+ }
+ walDisableBlocking(pWal);
+ }
+ }else if( pWal->writeLock ){
+ walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1);
+ pWal->writeLock = 0;
+ }
+ return rc;
+}
+#else
+# define walEnableBlocking(x,y) 0
+# define walDisableBlocking(x)
+#endif /* ifdef SQLITE_ENABLE_SETLK_TIMEOUT */
+
/*
** Begin a read transaction on the database.
**
@@ -2754,14 +2813,6 @@ int sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){
int bChanged = 0;
WalIndexHdr *pSnapshot = pWal->pSnapshot;
if( pSnapshot ){
-#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
- int busyTimeout = pWal->dbSnapshot->busyTimeout;
- if( busyTimeout ){
- int tmout = busyTimeout;
- sqlite3OsFileControl(pWal->pDbFd,SQLITE_FCNTL_LOCK_TIMEOUT,(void*)&tmout);
- }
-#endif
-
if( memcmp(pSnapshot, &pWal->hdr, sizeof(WalIndexHdr))!=0 ){
bChanged = 1;
}
@@ -2774,14 +2825,9 @@ int sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){
** its intent. To avoid the race condition this leads to, ensure that
** there is no checkpointer process by taking a shared CKPT lock
** before checking pInfo->nBackfillAttempted. */
+ walEnableBlocking(pWal->dbSnapshot, pWal);
rc = walLockShared(pWal, WAL_CKPT_LOCK);
-
-#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
- if( busyTimeout ){
- int tmout = 0;
- sqlite3OsFileControl(pWal->pDbFd,SQLITE_FCNTL_LOCK_TIMEOUT,(void*)&tmout);
- }
-#endif
+ walDisableBlocking(pWal);
if( rc!=SQLITE_OK ){
return rc;
@@ -2861,8 +2907,8 @@ int sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){
** read-lock.
*/
void sqlite3WalEndReadTransaction(Wal *pWal){
- sqlite3WalEndWriteTransaction(pWal);
if( pWal->readLock>=0 ){
+ sqlite3WalEndWriteTransaction(pWal);
walUnlockShared(pWal, WAL_READ_LOCK(pWal->readLock));
pWal->readLock = -1;
}
@@ -3022,6 +3068,16 @@ Pgno sqlite3WalDbsize(Wal *pWal){
int sqlite3WalBeginWriteTransaction(Wal *pWal){
int rc;
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ /* If the write-lock is already held, then it was obtained before the
+ ** read-transaction was even opened, making this call a no-op.
+ ** Return early. */
+ if( pWal->writeLock ){
+ assert( !memcmp(&pWal->hdr,(void *)walIndexHdr(pWal),sizeof(WalIndexHdr)) );
+ return SQLITE_OK;
+ }
+#endif
+
/* Cannot start a write transaction without first holding a read
** transaction. */
assert( pWal->readLock>=0 );
@@ -3587,9 +3643,6 @@ int sqlite3WalCheckpoint(
int isChanged = 0; /* True if a new wal-index header is loaded */
int eMode2 = eMode; /* Mode to pass to walCheckpoint() */
int (*xBusy2)(void*) = xBusy; /* Busy handler for eMode2 */
-#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
- int bSetLk = 0;
-#endif
assert( pWal->ckptLock==0 );
assert( pWal->writeLock==0 );
@@ -3601,18 +3654,11 @@ int sqlite3WalCheckpoint(
if( pWal->readOnly ) return SQLITE_READONLY;
WALTRACE(("WAL%p: checkpoint begins\n", pWal));
-#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
- if( db->busyTimeout ){
- int tmout = db->busyTimeout;
- sqlite3_file *fd = pWal->pDbFd;
- if( SQLITE_OK==
- sqlite3OsFileControl(fd, SQLITE_FCNTL_LOCK_TIMEOUT, (void*)&tmout)
- ){
- xBusy2 = 0;
- bSetLk = 1;
- }
+ /* Enable blocking locks, if possible. If blocking locks are successfully
+ ** enabled, set xBusy2=0 so that the busy-handler is never invoked. */
+ if( walEnableBlocking(db, pWal) ){
+ xBusy2 = 0;
}
-#endif
/* IMPLEMENTATION-OF: R-62028-47212 All calls obtain an exclusive
** "checkpoint" lock on the database file.
@@ -3684,13 +3730,7 @@ int sqlite3WalCheckpoint(
memset(&pWal->hdr, 0, sizeof(WalIndexHdr));
}
-#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
- if( bSetLk ){
- int tmout = 0;
- sqlite3_file *fd = pWal->pDbFd;
- sqlite3OsFileControl(fd, SQLITE_FCNTL_LOCK_TIMEOUT, (void*)&tmout);
- }
-#endif
+ walDisableBlocking(pWal);
/* Release the locks. */
sqlite3WalEndWriteTransaction(pWal);
diff --git a/src/wal.h b/src/wal.h
index bebd64370..99ebc937a 100644
--- a/src/wal.h
+++ b/src/wal.h
@@ -146,5 +146,9 @@ int sqlite3WalFramesize(Wal *pWal);
/* Return the sqlite3_file object for the WAL file */
sqlite3_file *sqlite3WalFile(Wal *pWal);
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+int sqlite3WalWriteLock(sqlite3 *db, Wal *pWal, int bLock);
+#endif
+
#endif /* ifndef SQLITE_OMIT_WAL */
#endif /* SQLITE_WAL_H */