]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Rework the SQLITE_MUTEXFREE_SHMLOCK code to reduce contention.
authordan <dan@noemail.net>
Mon, 10 Dec 2018 15:24:29 +0000 (15:24 +0000)
committerdan <dan@noemail.net>
Mon, 10 Dec 2018 15:24:29 +0000 (15:24 +0000)
FossilOrigin-Name: d9157dd176a2d18c6e02a2a0c7e16cef2da43bf44be9765e0363f34aebad23e9

manifest
manifest.uuid
src/os_unix.c
src/test_superlock.c
src/wal.c

index 7737a87ca328a373cd8a3b83dc320ef12be4d9be..30cc81f0edbfb989ac76c213c209ae4f15756f4c 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-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
@@ -491,7 +491,7 @@ F src/os.c 8aeb0b0f40f8f5b0da03fe49706695adaf42d2f516ab95abc72e86c245e119de
 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
@@ -558,7 +558,7 @@ F src/test_rtree.c 671f3fae50ff116ef2e32a3bf1fe21b5615b4b7b
 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
@@ -589,7 +589,7 @@ F src/vdbesort.c 90aad5a92608f2dd771c96749beabdb562c9d881131a860a7a5bccf66dc3be7
 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
@@ -1783,8 +1783,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 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
index 904b45f5cdc0e0061da5b2f520ae33ec53bfc650..8342dba98b44ddb570febe4e0d8b5540631755c0 100644 (file)
@@ -1 +1 @@
-a8c5fd86ce1d9b5815f82106f2e44797b743bbb3c44aee5f835ce8d278caf8f7
\ No newline at end of file
+d9157dd176a2d18c6e02a2a0c7e16cef2da43bf44be9765e0363f34aebad23e9
\ No newline at end of file
index 0c2cb98bdf8933df630771ba9478eb2ebc0c3992..7c31d7251106a2069a5be3f0371d5e182df4e3ac 100644 (file)
@@ -48,6 +48,8 @@
 
 /* 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
@@ -4255,7 +4257,10 @@ struct unixShmNode {
   ** 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
 };
 
@@ -4294,6 +4299,9 @@ struct unixShm {
   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
 };
 
 /*
@@ -4798,6 +4806,83 @@ shmpage_out:
   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.
 **
@@ -4831,59 +4916,19 @@ static int unixShmLock(
   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 ){
index 45d0d623a03b4cbf19ada28e754267b0b483e810..d677ce5378d0aac29a9f6d7a1028b2cfb35ad82d 100644 (file)
@@ -41,6 +41,8 @@ typedef struct SuperlockBusy SuperlockBusy;
 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;
 
@@ -107,12 +109,13 @@ static int superlockShmLock(
 ** 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);
@@ -121,8 +124,10 @@ static int superlockWalLock(
   /* 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
@@ -139,7 +144,9 @@ static int superlockWalLock(
   ** 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;
 }
 
@@ -156,8 +163,14 @@ void sqlite3demo_superunlock(void *pLock){
     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);
@@ -232,7 +245,7 @@ int sqlite3demo_superlock(
     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);
       }
     }
   }
index 4088bf2ec851aeecf4cbb50a625bbecb27ed9832..2883bd2d166ae8fbf4413f67281e6b9d0fcc61cf 100644 (file)
--- a/src/wal.c
+++ b/src/wal.c
@@ -2777,19 +2777,14 @@ int sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){
       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
@@ -2808,8 +2803,6 @@ int sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){
           rc = SQLITE_ERROR_SNAPSHOT;
         }
 
-        /* Release the shared CKPT lock obtained above. */
-        walUnlockShared(pWal, WAL_CKPT_LOCK);
         pWal->minFrame = 1;
       }