1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-12-21 13:38:01 +03:00

When SQLITE_ENABLE_SETLK_TIMEOUT is defined, use a separate mutex in os_unix.c for each shm locking slot.

FossilOrigin-Name: 4098df9652d90f2d22d5591d915d672c5413471f7916223510ba6fd932bfdd36
This commit is contained in:
dan
2023-11-17 17:10:37 +00:00
parent f2bb6ab3fb
commit 735e7ee62a
3 changed files with 163 additions and 79 deletions

View File

@@ -1,5 +1,5 @@
C Fix\sharmless\scompiler\swarnings\sin\sdebugging\scode. C When\sSQLITE_ENABLE_SETLK_TIMEOUT\sis\sdefined,\suse\sa\sseparate\smutex\sin\sos_unix.c\sfor\seach\sshm\slocking\sslot.
D 2023-11-17T12:22:42.597 D 2023-11-17T17:10:37.075
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -709,7 +709,7 @@ F src/os.h 1ff5ae51d339d0e30d8a9d814f4b8f8e448169304d83a7ed9db66a65732f3e63
F src/os_common.h 6c0eb8dd40ef3e12fe585a13e709710267a258e2c8dd1c40b1948a1d14582e06 F src/os_common.h 6c0eb8dd40ef3e12fe585a13e709710267a258e2c8dd1c40b1948a1d14582e06
F src/os_kv.c 4d39e1f1c180b11162c6dc4aa8ad34053873a639bac6baae23272fc03349986a F src/os_kv.c 4d39e1f1c180b11162c6dc4aa8ad34053873a639bac6baae23272fc03349986a
F src/os_setup.h 6011ad7af5db4e05155f385eb3a9b4470688de6f65d6166b8956e58a3d872107 F src/os_setup.h 6011ad7af5db4e05155f385eb3a9b4470688de6f65d6166b8956e58a3d872107
F src/os_unix.c f8a557ff5b387ec599e8b84b7341e5a45ebdd579da03788364a34b9a9567faeb F src/os_unix.c 1f6a930e8469b3709728690d089b55deb2fe605ba860941ddc21a2345e51bce4
F src/os_win.c 4a50a154aeebc66a1f8fb79c1ff6dd5fe3d005556533361e0d460d41cb6a45a8 F src/os_win.c 4a50a154aeebc66a1f8fb79c1ff6dd5fe3d005556533361e0d460d41cb6a45a8
F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
F src/pager.c 987ab3a2cd9065d62e9955474470ff733445e2357432a67e3d0f5a8f9313e334 F src/pager.c 987ab3a2cd9065d62e9955474470ff733445e2357432a67e3d0f5a8f9313e334
@@ -2140,8 +2140,11 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P a9443dbfbe25e588b4adddde664ddf482f19f71c704fbf356d49cf3a6135e7fb P ce6a75622ea5bca517bc6613e738aa670c9e1dd863596220eded5c2379c616c7
R dc0c90fa6e9a7208b4b533ec9a5980a8 R 277be03c0e13cb54ac8c8a3d23aba815
U drh T *branch * unix-setlk-timeout-mutexes
Z 5cf7a41ba5c0eb3c7c4392bc0e2744d2 T *sym-unix-setlk-timeout-mutexes *
T -sym-trunk *
U dan
Z 8cc8841ac15d12b07622be01120eb3c1
# Remove this line to create a well-formed Fossil manifest. # Remove this line to create a well-formed Fossil manifest.

View File

@@ -1 +1 @@
ce6a75622ea5bca517bc6613e738aa670c9e1dd863596220eded5c2379c616c7 4098df9652d90f2d22d5591d915d672c5413471f7916223510ba6fd932bfdd36

View File

@@ -4313,6 +4313,25 @@ static int unixGetpagesize(void){
** Either unixShmNode.pShmMutex must be held or unixShmNode.nRef==0 and ** Either unixShmNode.pShmMutex must be held or unixShmNode.nRef==0 and
** unixMutexHeld() is true when reading or writing any other field ** unixMutexHeld() is true when reading or writing any other field
** in this structure. ** in this structure.
**
** aLock[SQLITE_SHM_NLOCK]:
** This array records the various locks held by clients on each of the
** SQLITE_SHM_NLOCK slots. If the aLock[] entry is set to 0, then no
** locks are held by the process on this slot. If it is set to -1, then
** some client holds an EXCLUSIVE lock on the locking slot. If the aLock[]
** value is set to a positive value, then it is the number of shared
** locks currently held on the slot.
**
** aMutex[SQLITE_SHM_NLOCK]:
** Normally, when SQLITE_ENABLE_SETLK_TIMEOUT is not defined, mutex
** pShmMutex is used to protect the aLock[] array and the right to
** call fcntl() on unixShmNode.hShm to obtain or release locks.
**
** If SQLITE_ENABLE_SETLK_TIMEOUT is defined though, we use an array
** of mutexes - one for each locking slot. To read or write locking
** slot aLock[iSlot], the caller must hold the corresponding mutex
** aMutex[iSlot]. Similarly, to call fcntl() to obtain or release a
** lock corresponding to slot iSlot, mutex aMutex[iSlot] must be held.
*/ */
struct unixShmNode { struct unixShmNode {
unixInodeInfo *pInode; /* unixInodeInfo that owns this SHM node */ unixInodeInfo *pInode; /* unixInodeInfo that owns this SHM node */
@@ -4326,10 +4345,11 @@ struct unixShmNode {
char **apRegion; /* Array of mapped shared-memory regions */ char **apRegion; /* Array of mapped shared-memory regions */
int nRef; /* Number of unixShm objects pointing to this */ int nRef; /* Number of unixShm objects pointing to this */
unixShm *pFirst; /* All unixShm objects pointing to this */ unixShm *pFirst; /* All unixShm objects pointing to this */
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
sqlite3_mutex *aMutex[SQLITE_SHM_NLOCK];
#endif
int aLock[SQLITE_SHM_NLOCK]; /* # shared locks on slot, -1==excl lock */ int aLock[SQLITE_SHM_NLOCK]; /* # shared locks on slot, -1==excl lock */
#ifdef SQLITE_DEBUG #ifdef SQLITE_DEBUG
u8 exclMask; /* Mask of exclusive locks held */
u8 sharedMask; /* Mask of shared locks held */
u8 nextShmId; /* Next available unixShm.id value */ u8 nextShmId; /* Next available unixShm.id value */
#endif #endif
}; };
@@ -4412,16 +4432,29 @@ static int unixShmSystemLock(
struct flock f; /* The posix advisory locking structure */ struct flock f; /* The posix advisory locking structure */
int rc = SQLITE_OK; /* Result code form fcntl() */ int rc = SQLITE_OK; /* Result code form fcntl() */
/* Access to the unixShmNode object is serialized by the caller */
pShmNode = pFile->pInode->pShmNode; pShmNode = pFile->pInode->pShmNode;
assert( pShmNode->nRef==0 || sqlite3_mutex_held(pShmNode->pShmMutex) );
assert( pShmNode->nRef>0 || unixMutexHeld() ); /* Assert that the correct mutex or mutexes are held. */
if( pShmNode->nRef==0 ){
assert( ofst==UNIX_SHM_DMS && n==1 && unixMutexHeld() );
}else{
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
int ii;
for(ii=ofst-UNIX_SHM_BASE; ii<ofst-UNIX_SHM_BASE+n; ii++){
assert( sqlite3_mutex_held(pShmNode->aMutex[ii]) );
}
#else
assert( sqlite3_mutex_held(pShmNode->pShmMutex) );
assert( pShmNode->nRef>0 );
#endif
}
/* Shared locks never span more than one byte */ /* Shared locks never span more than one byte */
assert( n==1 || lockType!=F_RDLCK ); assert( n==1 || lockType!=F_RDLCK );
/* Locks are within range */ /* Locks are within range */
assert( n>=1 && n<=SQLITE_SHM_NLOCK ); assert( n>=1 && n<=SQLITE_SHM_NLOCK );
assert( ofst>=UNIX_SHM_BASE && ofst<=(UNIX_SHM_DMS+SQLITE_SHM_NLOCK) );
if( pShmNode->hShm>=0 ){ if( pShmNode->hShm>=0 ){
int res; int res;
@@ -4440,39 +4473,28 @@ static int unixShmSystemLock(
} }
} }
/* Update the global lock state and do debug tracing */ /* Do debug tracing */
#ifdef SQLITE_DEBUG #ifdef SQLITE_DEBUG
{ u16 mask;
OSTRACE(("SHM-LOCK ")); OSTRACE(("SHM-LOCK "));
mask = ofst>31 ? 0xffff : (1<<(ofst+n)) - (1<<ofst);
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
if( lockType==F_UNLCK ){ if( lockType==F_UNLCK ){
OSTRACE(("unlock %d ok", ofst)); OSTRACE(("unlock %d..%d ok\n", ofst, ofst+n-1));
pShmNode->exclMask &= ~mask;
pShmNode->sharedMask &= ~mask;
}else if( lockType==F_RDLCK ){ }else if( lockType==F_RDLCK ){
OSTRACE(("read-lock %d ok", ofst)); OSTRACE(("read-lock %d..%d ok\n", ofst, ofst+n-1));
pShmNode->exclMask &= ~mask;
pShmNode->sharedMask |= mask;
}else{ }else{
assert( lockType==F_WRLCK ); assert( lockType==F_WRLCK );
OSTRACE(("write-lock %d ok", ofst)); OSTRACE(("write-lock %d..%d ok\n", ofst, ofst+n-1));
pShmNode->exclMask |= mask;
pShmNode->sharedMask &= ~mask;
} }
}else{ }else{
if( lockType==F_UNLCK ){ if( lockType==F_UNLCK ){
OSTRACE(("unlock %d failed", ofst)); OSTRACE(("unlock %d..%d failed\n", ofst, ofst+n-1));
}else if( lockType==F_RDLCK ){ }else if( lockType==F_RDLCK ){
OSTRACE(("read-lock failed")); OSTRACE(("read-lock %d..%d failed\n", ofst, ofst+n-1));
}else{ }else{
assert( lockType==F_WRLCK ); assert( lockType==F_WRLCK );
OSTRACE(("write-lock %d failed", ofst)); OSTRACE(("write-lock %d..%d failed\n", ofst, ofst+n-1));
} }
} }
OSTRACE((" - afterwards %03x,%03x\n",
pShmNode->sharedMask, pShmNode->exclMask));
}
#endif #endif
return rc; return rc;
@@ -4509,6 +4531,11 @@ static void unixShmPurge(unixFile *pFd){
int i; int i;
assert( p->pInode==pFd->pInode ); assert( p->pInode==pFd->pInode );
sqlite3_mutex_free(p->pShmMutex); sqlite3_mutex_free(p->pShmMutex);
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
for(i=0; i<SQLITE_SHM_NLOCK; i++){
sqlite3_mutex_free(p->aMutex[i]);
}
#endif
for(i=0; i<p->nRegion; i+=nShmPerMap){ for(i=0; i<p->nRegion; i+=nShmPerMap){
if( p->hShm>=0 ){ if( p->hShm>=0 ){
osMunmap(p->apRegion[i], p->szRegion); osMunmap(p->apRegion[i], p->szRegion);
@@ -4689,6 +4716,18 @@ static int unixOpenSharedMemory(unixFile *pDbFd){
rc = SQLITE_NOMEM_BKPT; rc = SQLITE_NOMEM_BKPT;
goto shm_open_err; goto shm_open_err;
} }
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
{
int ii;
for(ii=0; ii<SQLITE_SHM_NLOCK; ii++){
pShmNode->aMutex[ii] = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
if( pShmNode->aMutex[ii]==0 ){
rc = SQLITE_NOMEM_BKPT;
goto shm_open_err;
}
}
}
#endif
} }
if( pInode->bProcessLock==0 ){ if( pInode->bProcessLock==0 ){
@@ -4910,9 +4949,11 @@ shmpage_out:
*/ */
#ifdef SQLITE_DEBUG #ifdef SQLITE_DEBUG
static int assertLockingArrayOk(unixShmNode *pShmNode){ static int assertLockingArrayOk(unixShmNode *pShmNode){
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
return 1;
#else
unixShm *pX; unixShm *pX;
int aLock[SQLITE_SHM_NLOCK]; int aLock[SQLITE_SHM_NLOCK];
assert( sqlite3_mutex_held(pShmNode->pShmMutex) );
memset(aLock, 0, sizeof(aLock)); memset(aLock, 0, sizeof(aLock));
for(pX=pShmNode->pFirst; pX; pX=pX->pNext){ for(pX=pShmNode->pFirst; pX; pX=pX->pNext){
@@ -4930,13 +4971,14 @@ static int assertLockingArrayOk(unixShmNode *pShmNode){
assert( 0==memcmp(pShmNode->aLock, aLock, sizeof(aLock)) ); assert( 0==memcmp(pShmNode->aLock, aLock, sizeof(aLock)) );
return (memcmp(pShmNode->aLock, aLock, sizeof(aLock))==0); return (memcmp(pShmNode->aLock, aLock, sizeof(aLock))==0);
#endif
} }
#endif #endif
/* /*
** Change the lock state for a shared-memory segment. ** Change the lock state for a shared-memory segment.
** **
** Note that the relationship between SHAREd and EXCLUSIVE locks is a little ** Note that the relationship between SHARED and EXCLUSIVE locks is a little
** different here than in posix. In xShmLock(), one can go from unlocked ** different here than in posix. In xShmLock(), one can go from unlocked
** to shared and back or from unlocked to exclusive and back. But one may ** to shared and back or from unlocked to exclusive and back. But one may
** not go from shared to exclusive or from exclusive to shared. ** not go from shared to exclusive or from exclusive to shared.
@@ -4951,7 +4993,7 @@ static int unixShmLock(
unixShm *p; /* The shared memory being locked */ unixShm *p; /* The shared memory being locked */
unixShmNode *pShmNode; /* The underlying file iNode */ unixShmNode *pShmNode; /* The underlying file iNode */
int rc = SQLITE_OK; /* Result code */ int rc = SQLITE_OK; /* Result code */
u16 mask; /* Mask of locks to take or release */ u16 mask = (1<<(ofst+n)) - (1<<ofst); /* Mask of locks to take or release */
int *aLock; int *aLock;
p = pDbFd->pShm; p = pDbFd->pShm;
@@ -4997,18 +5039,51 @@ static int unixShmLock(
} }
#endif #endif
mask = (1<<(ofst+n)) - (1<<ofst); /* Check if there is any work to do. There are three cases:
assert( n>1 || mask==(1<<ofst) ); **
sqlite3_mutex_enter(pShmNode->pShmMutex); ** a) An unlock operation where there are locks to unlock,
assert( assertLockingArrayOk(pShmNode) ); ** b) An shared lock where the requested lock is not already held
if( flags & SQLITE_SHM_UNLOCK ){ ** c) An exclusive lock where the requested lock is not already held
if( (p->exclMask|p->sharedMask) & mask ){ **
int ii; ** The SQLite core never requests an exclusive lock that it already holds.
int bUnlock = 1; ** This is assert()ed below.
*/
assert( flags!=(SQLITE_SHM_EXCLUSIVE|SQLITE_SHM_LOCK)
|| 0==(p->exclMask & mask)
);
if( ((flags & SQLITE_SHM_UNLOCK) && ((p->exclMask|p->sharedMask) & mask))
|| (flags==(SQLITE_SHM_SHARED|SQLITE_SHM_LOCK) && 0==(p->sharedMask & mask))
|| (flags==(SQLITE_SHM_EXCLUSIVE|SQLITE_SHM_LOCK))
){
for(ii=ofst; ii<ofst+n; ii++){ /* Take the required mutexes */
if( aLock[ii]>((p->sharedMask & (1<<ii)) ? 1 : 0) ){ #ifdef SQLITE_ENABLE_SETLK_TIMEOUT
int iMutex;
for(iMutex=ofst; iMutex<ofst+n; iMutex++){
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; bUnlock = 0;
aLock[ofst]--;
p->sharedMask &= ~mask;
} }
} }
@@ -5016,23 +5091,15 @@ static int unixShmLock(
rc = unixShmSystemLock(pDbFd, F_UNLCK, ofst+UNIX_SHM_BASE, n); rc = unixShmSystemLock(pDbFd, F_UNLCK, ofst+UNIX_SHM_BASE, n);
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
memset(&aLock[ofst], 0, sizeof(int)*n); memset(&aLock[ofst], 0, sizeof(int)*n);
p->sharedMask &= ~mask;
p->exclMask &= ~mask;
} }
}else if( ALWAYS(p->sharedMask & (1<<ofst)) ){
assert( n==1 && aLock[ofst]>1 );
aLock[ofst]--;
} }
}else if( flags & SQLITE_SHM_SHARED ){
/* Case (b) - a shared lock. */
/* Undo the local locks */
if( rc==SQLITE_OK ){
p->exclMask &= ~mask;
p->sharedMask &= ~mask;
}
}
}else if( flags & SQLITE_SHM_SHARED ){
assert( n==1 );
assert( (p->exclMask & (1<<ofst))==0 );
if( (p->sharedMask & mask)==0 ){
if( aLock[ofst]<0 ){ if( aLock[ofst]<0 ){
/* An exclusive lock is held by some other connection. BUSY. */
rc = SQLITE_BUSY; rc = SQLITE_BUSY;
}else if( aLock[ofst]==0 ){ }else if( aLock[ofst]==0 ){
rc = unixShmSystemLock(pDbFd, F_RDLCK, ofst+UNIX_SHM_BASE, n); rc = unixShmSystemLock(pDbFd, F_RDLCK, ofst+UNIX_SHM_BASE, n);
@@ -5043,34 +5110,48 @@ static int unixShmLock(
p->sharedMask |= mask; p->sharedMask |= mask;
aLock[ofst]++; aLock[ofst]++;
} }
} }else{
}else{ /* Case (c) - an exclusive lock. */
/* Make sure no sibling connections hold locks that will block this int ii;
** lock. If any do, return SQLITE_BUSY right away. */
int ii;
for(ii=ofst; ii<ofst+n; ii++){
assert( (p->sharedMask & mask)==0 );
if( ALWAYS((p->exclMask & (1<<ii))==0) && aLock[ii] ){
rc = SQLITE_BUSY;
break;
}
}
/* Get the exclusive locks at the system level. Then if successful assert( flags==(SQLITE_SHM_LOCK|SQLITE_SHM_EXCLUSIVE) );
** also update the in-memory values. */ assert( (p->sharedMask & mask)==0 );
if( rc==SQLITE_OK ){ assert( (p->exclMask & mask)==0 );
rc = unixShmSystemLock(pDbFd, F_WRLCK, ofst+UNIX_SHM_BASE, n);
/* 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 ){ if( rc==SQLITE_OK ){
assert( (p->sharedMask & mask)==0 ); rc = unixShmSystemLock(pDbFd, F_WRLCK, ofst+UNIX_SHM_BASE, n);
p->exclMask |= mask; if( rc==SQLITE_OK ){
for(ii=ofst; ii<ofst+n; ii++){ p->exclMask |= mask;
aLock[ii] = -1; for(ii=ofst; ii<ofst+n; ii++){
aLock[ii] = -1;
}
} }
} }
} }
assert( assertLockingArrayOk(pShmNode) );
/* Drop the mutexes acquired above. */
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
for(iMutex=ofst; iMutex<ofst+n; iMutex++){
sqlite3_mutex_leave(pShmNode->aMutex[iMutex]);
}
#else
sqlite3_mutex_leave(pShmNode->pShmMutex);
#endif
} }
assert( assertLockingArrayOk(pShmNode) );
sqlite3_mutex_leave(pShmNode->pShmMutex);
OSTRACE(("SHM-LOCK shmid-%d, pid-%d got %03x,%03x\n", OSTRACE(("SHM-LOCK shmid-%d, pid-%d got %03x,%03x\n",
p->id, osGetpid(0), p->sharedMask, p->exclMask)); p->id, osGetpid(0), p->sharedMask, p->exclMask));
return rc; return rc;