aboutsummaryrefslogtreecommitdiff
path: root/src/os_unix.c
diff options
context:
space:
mode:
authordan <Dan Kennedy>2023-11-18 17:20:04 +0000
committerdan <Dan Kennedy>2023-11-18 17:20:04 +0000
commit046a7f94930f1c2f4af8f8ac47a3126be81980c7 (patch)
tree3614c046a7198fa402283dbbc1689e96280f5989 /src/os_unix.c
parent52c41ed41bcadfce1c0b5bb18501a283aedc0293 (diff)
downloadsqlite-046a7f94930f1c2f4af8f8ac47a3126be81980c7.tar.gz
sqlite-046a7f94930f1c2f4af8f8ac47a3126be81980c7.zip
When ENABLE_SETLK is defined, avoid ever blocking on the lock mutex in os_unix.c when requesting an exclusive lock.
FossilOrigin-Name: eb36d475e91bfdbf4a18b6fd9751fbcecf15d960dcd1c00d2d18b5bf1d7503fe
Diffstat (limited to 'src/os_unix.c')
-rw-r--r--src/os_unix.c158
1 files changed, 89 insertions, 69 deletions
diff --git a/src/os_unix.c b/src/os_unix.c
index 82c81a4b1..dab03c97f 100644
--- a/src/os_unix.c
+++ b/src/os_unix.c
@@ -5056,95 +5056,115 @@ static int unixShmLock(
|| (flags==(SQLITE_SHM_EXCLUSIVE|SQLITE_SHM_LOCK))
){
- /* Take the required mutexes */
+ /* Take the required mutexes. In SETLK_TIMEOUT mode (blocking locks), if
+ ** this is an attempt on an exclusive lock use sqlite3_mutex_try(). If any
+ ** other thread is holding this mutex, then it is either holding or about
+ ** to hold a lock exclusive to the one being requested, and we may
+ ** therefore return SQLITE_BUSY to the caller.
+ **
+ ** Doing this prevents some deadlock scenarios. For example, thread 1 may
+ ** be a checkpointer blocked waiting on the WRITER lock. And thread 2
+ ** may be a normal SQL client upgrading to a write transaction. In this
+ ** case thread 2 does a non-blocking request for the WRITER lock. But -
+ ** if it were to use sqlite3_mutex_enter() then it would effectively
+ ** become a (doomed) blocking request, as thread 2 would block until thread
+ ** 1 obtained WRITER and released the mutex. Since thread 2 already holds
+ ** a lock on a read-locking slot at this point, this breaks the
+ ** anti-deadlock rules (see above). */
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
int iMutex;
for(iMutex=ofst; iMutex<ofst+n; iMutex++){
- sqlite3_mutex_enter(pShmNode->aMutex[iMutex]);
+ if( flags==(SQLITE_SHM_LOCK|SQLITE_SHM_EXCLUSIVE) ){
+ rc = sqlite3_mutex_try(pShmNode->aMutex[iMutex]);
+ if( rc!=SQLITE_OK ) break;
+ }else{
+ sqlite3_mutex_enter(pShmNode->aMutex[iMutex]);
+ }
}
#else
sqlite3_mutex_enter(pShmNode->pShmMutex);
#endif
- if( flags & SQLITE_SHM_UNLOCK ){
- /* Case (a) - unlock. */
- int bUnlock = 1;
- assert( (p->exclMask & p->sharedMask)==0 );
- assert( (flags & SQLITE_SHM_EXCLUSIVE)==0 || (p->exclMask & mask)==mask );
- assert( (flags & SQLITE_SHM_SHARED)==0 || (p->sharedMask & mask)==mask );
-
- /* If this is a SHARED lock being unlocked, it is possible that other
- ** clients within this process are holding the same SHARED lock. In
- ** this case, set bUnlock to 0 so that the posix lock is not removed
- ** from the file-descriptor below. */
- if( flags & SQLITE_SHM_SHARED ){
- assert( n==1 );
- assert( aLock[ofst]>=1 );
- if( aLock[ofst]>1 ){
- bUnlock = 0;
- aLock[ofst]--;
- p->sharedMask &= ~mask;
+ if( rc==SQLITE_OK ){
+ if( flags & SQLITE_SHM_UNLOCK ){
+ /* Case (a) - unlock. */
+ int bUnlock = 1;
+ assert( (p->exclMask & p->sharedMask)==0 );
+ assert( !(flags & SQLITE_SHM_EXCLUSIVE) || (p->exclMask & mask)==mask );
+ assert( !(flags & SQLITE_SHM_SHARED) || (p->sharedMask & mask)==mask );
+
+ /* If this is a SHARED lock being unlocked, it is possible that other
+ ** clients within this process are holding the same SHARED lock. In
+ ** this case, set bUnlock to 0 so that the posix lock is not removed
+ ** from the file-descriptor below. */
+ if( flags & SQLITE_SHM_SHARED ){
+ assert( n==1 );
+ assert( aLock[ofst]>=1 );
+ if( aLock[ofst]>1 ){
+ bUnlock = 0;
+ aLock[ofst]--;
+ p->sharedMask &= ~mask;
+ }
}
- }
-
- if( bUnlock ){
- rc = unixShmSystemLock(pDbFd, F_UNLCK, ofst+UNIX_SHM_BASE, n);
- if( rc==SQLITE_OK ){
- memset(&aLock[ofst], 0, sizeof(int)*n);
- p->sharedMask &= ~mask;
- p->exclMask &= ~mask;
+
+ if( bUnlock ){
+ rc = unixShmSystemLock(pDbFd, F_UNLCK, ofst+UNIX_SHM_BASE, n);
+ if( rc==SQLITE_OK ){
+ memset(&aLock[ofst], 0, sizeof(int)*n);
+ p->sharedMask &= ~mask;
+ p->exclMask &= ~mask;
+ }
}
- }
- }else if( flags & SQLITE_SHM_SHARED ){
- /* Case (b) - a shared lock. */
-
- if( aLock[ofst]<0 ){
- /* An exclusive lock is held by some other connection. BUSY. */
- rc = SQLITE_BUSY;
- }else if( aLock[ofst]==0 ){
- rc = unixShmSystemLock(pDbFd, F_RDLCK, ofst+UNIX_SHM_BASE, n);
- }
-
- /* Get the local shared locks */
- if( rc==SQLITE_OK ){
- p->sharedMask |= mask;
- aLock[ofst]++;
- }
- }else{
- /* Case (c) - an exclusive lock. */
- int ii;
-
- assert( flags==(SQLITE_SHM_LOCK|SQLITE_SHM_EXCLUSIVE) );
- assert( (p->sharedMask & mask)==0 );
- assert( (p->exclMask & mask)==0 );
-
- /* Make sure no sibling connections hold locks that will block this
- ** lock. If any do, return SQLITE_BUSY right away. */
- for(ii=ofst; ii<ofst+n; ii++){
- if( aLock[ii] ){
+ }else if( flags & SQLITE_SHM_SHARED ){
+ /* Case (b) - a shared lock. */
+
+ if( aLock[ofst]<0 ){
+ /* An exclusive lock is held by some other connection. BUSY. */
rc = SQLITE_BUSY;
- break;
+ }else if( aLock[ofst]==0 ){
+ rc = unixShmSystemLock(pDbFd, F_RDLCK, ofst+UNIX_SHM_BASE, n);
}
- }
-
- /* Get the exclusive locks at the system level. Then if successful
- ** also update the in-memory values. */
- if( rc==SQLITE_OK ){
- rc = unixShmSystemLock(pDbFd, F_WRLCK, ofst+UNIX_SHM_BASE, n);
+
+ /* Get the local shared locks */
if( rc==SQLITE_OK ){
- p->exclMask |= mask;
- for(ii=ofst; ii<ofst+n; ii++){
- aLock[ii] = -1;
+ p->sharedMask |= mask;
+ aLock[ofst]++;
+ }
+ }else{
+ /* Case (c) - an exclusive lock. */
+ int ii;
+
+ assert( flags==(SQLITE_SHM_LOCK|SQLITE_SHM_EXCLUSIVE) );
+ assert( (p->sharedMask & mask)==0 );
+ assert( (p->exclMask & mask)==0 );
+
+ /* Make sure no sibling connections hold locks that will block this
+ ** lock. If any do, return SQLITE_BUSY right away. */
+ for(ii=ofst; ii<ofst+n; ii++){
+ if( aLock[ii] ){
+ rc = SQLITE_BUSY;
+ break;
+ }
+ }
+
+ /* Get the exclusive locks at the system level. Then if successful
+ ** also update the in-memory values. */
+ if( rc==SQLITE_OK ){
+ rc = unixShmSystemLock(pDbFd, F_WRLCK, ofst+UNIX_SHM_BASE, n);
+ if( rc==SQLITE_OK ){
+ p->exclMask |= mask;
+ for(ii=ofst; ii<ofst+n; ii++){
+ aLock[ii] = -1;
+ }
}
}
}
+ assert( assertLockingArrayOk(pShmNode) );
}
-
- assert( assertLockingArrayOk(pShmNode) );
/* Drop the mutexes acquired above. */
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
- for(iMutex=ofst; iMutex<ofst+n; iMutex++){
+ for(iMutex--; iMutex>=ofst; iMutex--){
sqlite3_mutex_leave(pShmNode->aMutex[iMutex]);
}
#else