-C Avoid\sa\smutex\sin-and-out\sin\sunixShmBarrier()\son\sthis\sbranch.\sUse\n__sync_synchronize()\sinstead.
-D 2018-12-10T09:45:38.153
+C Rework\sthe\sSQLITE_MUTEXFREE_SHMLOCK\scode\sto\sreduce\scontention.
+D 2018-12-10T15:24:29.922
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F Makefile.in 68d0ba0f0b533d5bc84c78c13a6ce84ee81183a67014caa47a969e67f028fa1c
F src/os.h 48388821692e87da174ea198bf96b1b2d9d83be5dfc908f673ee21fafbe0d432
F src/os_common.h b2f4707a603e36811d9b1a13278bffd757857b85
F src/os_setup.h 0dbaea40a7d36bf311613d31342e0b99e2536586
-F src/os_unix.c 8526ea4590b158dfa1b62831da9b50337d131f375aaa27c61567b345e9760cd2
+F src/os_unix.c 4b21c5148ca49fa136a6f404121080deee3e14f14ce07ea3cd132e15d40e5a93
F src/os_win.c 85d9e532d0444ab6c16d7431490c2e279e282aa0917b0e988996b1ae0de5c5a0
F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
F src/pager.c 75e0f3cfa3962c714f519f8a3d1e67ecca1c91de0e010a036b988e40ce9e4c73
F src/test_schema.c f575932cb6274d12147a77e13ea4b49d52408513
F src/test_server.c a2615049954cbb9cfb4a62e18e2f0616e4dc38fe
F src/test_sqllog.c 11e6ce7575f489155c604ac4b439f2ac1d3d5aef
-F src/test_superlock.c 4839644b9201da822f181c5bc406c0b2385f672e
+F src/test_superlock.c f4d4cc7319a608a54b7608158e8c7135fac19b88d6179e5bf17080e89d1f0278
F src/test_syscall.c 1073306ba2e9bfc886771871a13d3de281ed3939
F src/test_tclsh.c 06317648b0d85a85fd823f7973b55535c59a3156c1ef59394fe511f932cfa78d
F src/test_tclvar.c 33ff42149494a39c5fbb0df3d25d6fafb2f668888e41c0688d07273dcb268dfc
F src/vdbetrace.c 79d6dbbc479267b255a7de8080eee6e729928a0ef93ed9b0bfa5618875b48392
F src/vtab.c 70188a745dc4e57d26e942681ff4b2912b7c8249ad5de3f60f0677b4337bcfaa
F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
-F src/wal.c 3f4f653daf234fe713edbcbca3fec2350417d159d28801feabc702a22c4e213f
+F src/wal.c d1c4f4edd172d4b9320d785304bad3efc433baf672621653017434ab87f3f336
F src/wal.h 606292549f5a7be50b6227bd685fa76e3a4affad71bb8ac5ce4cb5c79f6a176a
F src/walker.c fb94aadc9099ff9c6506d0a8b88d51266005bcaa265403f3d7caf732a562eb66
F src/where.c 3818e8a736a05d2cb194e64399af707e367fbcc5c251d785804d02eaf121288e
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 8f4cb9dd3396bcaaf85dcdb4e3ae3c96f28a4d71d72665c4abf7c221370be626
-Q +280d1a72dc269b4402853159949d7a905d414786074f5d80e951c9341ebbd1b5
-R 124b36ab6f5a94c22a4c2edeb34525f1
+P a8c5fd86ce1d9b5815f82106f2e44797b743bbb3c44aee5f835ce8d278caf8f7
+R 2c73dae6f22740de28ff76884e3353b6
U dan
-Z 3d15546b7a977bfe213c636690aee9c2
+Z 57d8b49adf3b9b02554cff5dcd73ea2d
-a8c5fd86ce1d9b5815f82106f2e44797b743bbb3c44aee5f835ce8d278caf8f7
\ No newline at end of file
+d9157dd176a2d18c6e02a2a0c7e16cef2da43bf44be9765e0363f34aebad23e9
\ No newline at end of file
/* Turn this feature on in all builds for now */
#define SQLITE_MUTEXFREE_SHMLOCK 1
+#define SQLITE_MFS_NSHARD 5
+#define SQLITE_MFS_EXCLUSIVE 255
/*
** There are various methods for file locking used for concurrency
** a non-zero value smaller than 0xFF, then they represent the total
** number of read-locks held on the slot. There is no way to distinguish
** between a write-lock and 255 read-locks. */
- u64 lockmask;
+ struct LockingSlot {
+ u32 nLock;
+ u64 aPadding[7];
+ } aMFSlot[3 + SQLITE_MFS_NSHARD*5];
#endif
};
u8 id; /* Id of this connection within its unixShmNode */
u16 sharedMask; /* Mask of shared locks held */
u16 exclMask; /* Mask of exclusive locks held */
+#ifdef SQLITE_MUTEXFREE_SHMLOCK
+ u8 aMFCurrent[8]; /* Current slot used for each shared lock */
+#endif
};
/*
return rc;
}
+#ifdef SQLITE_MUTEXFREE_SHMLOCK
+static int unixMutexFreeShmlock(
+ unixFile *pFd, /* Database file holding the shared memory */
+ int ofst, /* First lock to acquire or release */
+ int n, /* Number of locks to acquire or release */
+ int flags /* What to do with the lock */
+){
+ struct LockMapEntry {
+ int iFirst;
+ int nSlot;
+ } aMap[9] = {
+ { 0, 1 },
+ { 1, 1 },
+ { 2, 1 },
+ { 3+0*SQLITE_MFS_NSHARD, SQLITE_MFS_NSHARD },
+ { 3+1*SQLITE_MFS_NSHARD, SQLITE_MFS_NSHARD },
+ { 3+2*SQLITE_MFS_NSHARD, SQLITE_MFS_NSHARD },
+ { 3+3*SQLITE_MFS_NSHARD, SQLITE_MFS_NSHARD },
+ { 3+4*SQLITE_MFS_NSHARD, SQLITE_MFS_NSHARD },
+ { 3+5*SQLITE_MFS_NSHARD, 0 },
+ };
+
+ unixShm *p = pFd->pShm; /* The shared memory being locked */
+ unixShm *pX; /* For looping over all siblings */
+ unixShmNode *pShmNode = p->pShmNode; /* The underlying file iNode */
+ int rc = SQLITE_OK;
+ int iIncr;
+ u16 mask; /* Mask of locks to take or release */
+
+ if( flags & SQLITE_SHM_SHARED ){
+ /* SHARED locks */
+ u32 iOld, iNew, *ptr;
+ int iIncr = -1;
+ if( (flags & SQLITE_SHM_UNLOCK)==0 ){
+ p->aMFCurrent[ofst] = (p->aMFCurrent[ofst] + 1) % aMap[ofst].nSlot;
+ iIncr = 1;
+ }
+ ptr = &pShmNode->aMFSlot[aMap[ofst].iFirst + p->aMFCurrent[ofst]].nLock;
+ do {
+ iOld = *ptr;
+ iNew = iOld + iIncr;
+ if( iNew>SQLITE_MFS_EXCLUSIVE ){
+ return SQLITE_BUSY;
+ }
+ }while( 0==unixCompareAndSwap(ptr, iOld, iNew) );
+ }else{
+ /* EXCLUSIVE locks */
+ int iFirst = aMap[ofst].iFirst;
+ int iLast = aMap[ofst+n].iFirst;
+ int i;
+ for(i=iFirst; i<iLast; i++){
+ u32 *ptr = &pShmNode->aMFSlot[i].nLock;
+ if( flags & SQLITE_SHM_UNLOCK ){
+ assert( (*ptr)==SQLITE_MFS_EXCLUSIVE );
+ *ptr = 0;
+ }else{
+ u32 iOld;
+ do {
+ iOld = *ptr;
+ if( iOld>0 ){
+ while( i>iFirst ){
+ i--;
+ pShmNode->aMFSlot[i].nLock = 0;
+ }
+ return SQLITE_BUSY;
+ }
+ }while( 0==unixCompareAndSwap(ptr, iOld, SQLITE_MFS_EXCLUSIVE) );
+ }
+ }
+ }
+
+ return SQLITE_OK;
+}
+#else
+# define unixMutexFreeShmlock(a,b,c,d) SQLITE_OK
+#endif
+
/*
** Change the lock state for a shared-memory segment.
**
assert( pShmNode->hShm>=0 || pDbFd->pInode->bProcessLock==1 );
assert( pShmNode->hShm<0 || pDbFd->pInode->bProcessLock==0 );
- mask = (1<<(ofst+n)) - (1<<ofst);
- assert( n>1 || mask==(1<<ofst) );
-
-#ifdef SQLITE_MUTEXFREE_SHMLOCK
if( pDbFd->pInode->bProcessLock ){
+ return unixMutexFreeShmlock(pDbFd, ofst, n, flags);
+ }
- while( 1 ){
- u64 lockmask = pShmNode->lockmask;
- u64 newmask = lockmask;
- int i;
- for(i=ofst; i<n+ofst; i++){
- int ix8 = i*8;
- u8 v = (lockmask >> (ix8)) & 0xFF;
- if( flags & SQLITE_SHM_UNLOCK ){
- if( flags & SQLITE_SHM_EXCLUSIVE ){
- if( p->exclMask & (1 << i) ){
- newmask = newmask & ~((u64)0xFF<<ix8);
- }
- }else{
- if( p->sharedMask & (1 << i) ){
- newmask = newmask & ~((u64)0xFF<<ix8) | ((u64)(v-1)<<ix8);
- }
- }
- }else{
- if( flags & SQLITE_SHM_EXCLUSIVE ){
- if( v ) return SQLITE_BUSY;
- if( (p->exclMask & (1 << i))==0 ){
- newmask = newmask | ((u64)0xFF<<ix8);
- }
- }else{
- if( v==0xFF ) return SQLITE_BUSY;
- if( (p->sharedMask & (1 << i))==0 ){
- newmask = newmask & ~((u64)0xFF<<ix8) | ((u64)(v+1)<<ix8);
- }
- }
- }
- }
-
- if( unixCompareAndSwap(&pShmNode->lockmask, lockmask, newmask) ) break;
- }
-
- if( flags & SQLITE_SHM_UNLOCK ){
- p->sharedMask &= ~mask;
- p->exclMask &= ~mask;
- }else if( flags & SQLITE_SHM_EXCLUSIVE ){
- p->exclMask |= mask;
- }else{
- p->sharedMask |= mask;
- }
-
- return SQLITE_OK;
+ mask = (1<<(ofst+n)) - (1<<ofst);
+ assert( n>1 || mask==(1<<ofst) );
+ if( flags & SQLITE_SHM_LOCK ){
+ assert( !(flags&SQLITE_SHM_SHARED) || (p->sharedMask&mask)==0 );
+ assert( !(flags&SQLITE_SHM_EXCLUSIVE) || !(p->exclMask&mask) );
+ }else{
+ assert( !(flags&SQLITE_SHM_SHARED) || (p->sharedMask&mask)==mask );
+ assert( !(flags&SQLITE_SHM_EXCLUSIVE) || (p->exclMask&mask)==mask );
}
-#endif
sqlite3_mutex_enter(pShmNode->pShmMutex);
if( flags & SQLITE_SHM_UNLOCK ){
struct Superlock {
sqlite3 *db; /* Database handle used to lock db */
int bWal; /* True if db is a WAL database */
+ int bRecoveryLocked; /* True if WAL RECOVERY lock is held */
+ int bReaderLocked; /* True if WAL READER locks are held */
};
typedef struct Superlock Superlock;
** Invoke the supplied busy-handler as required.
*/
static int superlockWalLock(
- sqlite3 *db, /* Database handle open on WAL database */
+ Superlock *pLock, /* Superlock handle */
SuperlockBusy *pBusy /* Busy handler wrapper object */
){
int rc; /* Return code */
sqlite3_file *fd = 0; /* Main database file handle */
void volatile *p = 0; /* Pointer to first page of shared memory */
+ sqlite3 *db = pLock->db;
/* Obtain a pointer to the sqlite3_file object open on the main db file. */
rc = sqlite3_file_control(db, "main", SQLITE_FCNTL_FILE_POINTER, (void *)&fd);
/* Obtain the "recovery" lock. Normally, this lock is only obtained by
** clients running database recovery.
*/
+ assert( pLock->bRecoveryLocked==0 );
rc = superlockShmLock(fd, 2, 1, pBusy);
if( rc!=SQLITE_OK ) return rc;
+ pLock->bRecoveryLocked = 1;
/* Zero the start of the first shared-memory page. This means that any
** clients that open read or write transactions from this point on will
** are held, it is guaranteed that there are no active reader, writer or
** checkpointer clients.
*/
+ assert( pLock->bReaderLocked==0 );
rc = superlockShmLock(fd, 3, SQLITE_SHM_NLOCK-3, pBusy);
+ if( rc==SQLITE_OK ) pLock->bReaderLocked = 1;
return rc;
}
sqlite3_file *fd = 0;
rc = sqlite3_file_control(p->db, "main", SQLITE_FCNTL_FILE_POINTER, (void *)&fd);
if( rc==SQLITE_OK ){
- fd->pMethods->xShmLock(fd, 2, 1, flags);
- fd->pMethods->xShmLock(fd, 3, SQLITE_SHM_NLOCK-3, flags);
+ if( p->bRecoveryLocked ){
+ fd->pMethods->xShmLock(fd, 2, 1, flags);
+ p->bRecoveryLocked = 0;
+ }
+ if( p->bReaderLocked ){
+ fd->pMethods->xShmLock(fd, 3, SQLITE_SHM_NLOCK-3, flags);
+ p->bReaderLocked = 0;
+ }
}
}
sqlite3_close(p->db);
if( SQLITE_OK==(rc = superlockIsWal(pLock)) && pLock->bWal ){
rc = sqlite3_exec(pLock->db, "COMMIT", 0, 0, 0);
if( rc==SQLITE_OK ){
- rc = superlockWalLock(pLock->db, &busy);
+ rc = superlockWalLock(pLock, &busy);
}
}
}
assert( pWal->readLock>0 || pWal->hdr.mxFrame==0 );
assert( pInfo->aReadMark[pWal->readLock]<=pSnapshot->mxFrame );
- /* It is possible that there is a checkpointer thread running
- ** concurrent with this code. If this is the case, it may be that the
- ** checkpointer has already determined that it will checkpoint
+ /* If it were possible for a checkpointer thread to run concurrent
+ ** with this code, it would be a problem. In this case, it could be
+ ** that the checkpointer has already determined that it will checkpoint
** snapshot X, where X is later in the wal file than pSnapshot, but
** has not yet set the pInfo->nBackfillAttempted variable to indicate
- ** 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.
- **
- ** TODO: Does the aReadMark[] lock prevent a checkpointer from doing
- ** this already?
- */
- rc = walLockShared(pWal, WAL_CKPT_LOCK);
+ ** its intent. Fortunately this is not possible, as the call to
+ ** sqlite3WalSnapshotOpen() that sets pWal->pSnapshot also takes a
+ ** SHARED lock on the checkpointer slot. */
if( rc==SQLITE_OK ){
/* Check that the wal file has not been wrapped. Assuming that it has
rc = SQLITE_ERROR_SNAPSHOT;
}
- /* Release the shared CKPT lock obtained above. */
- walUnlockShared(pWal, WAL_CKPT_LOCK);
pWal->minFrame = 1;
}