]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Fixes to ensure SQLITE_ENABLE_SETLK_TIMEOUT builds use a blocking lock
authordrh <>
Thu, 5 Jun 2025 11:57:20 +0000 (11:57 +0000)
committerdrh <>
Thu, 5 Jun 2025 11:57:20 +0000 (11:57 +0000)
and do not call xSleep() when (a) opening a snapshot transaction, and
(b) when blocked by another process running recovery.

FossilOrigin-Name: 8ac4525a2e3100bb5b9460cc49bb64f007911180d5f51461282b1de0201328b6

manifest
manifest.uuid
src/btree.c
src/os_unix.c
src/os_win.c
src/pager.c
src/wal.c
test/snapshot3.test
test/walsetlk_recover.test [new file with mode: 0644]
test/walsetlk_snapshot.test [new file with mode: 0644]

index cfbfa42d3f0bc0d3d0220cad69e46657ddf79a0d..a31084e34b98400e579e15d16ccd2b70d32cbce6 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Merge\sTEA\sREADME\supdates\sfrom\s[bf7be67e3f].\sNo\scode\schanges.
-D 2025-06-05T08:33:08.327
+C Fixes\sto\sensure\sSQLITE_ENABLE_SETLK_TIMEOUT\sbuilds\suse\sa\sblocking\slock\nand\sdo\snot\scall\sxSleep()\swhen\s(a)\sopening\sa\ssnapshot\stransaction,\sand\n(b)\swhen\sblocked\sby\sanother\sprocess\srunning\srecovery.
+D 2025-06-05T11:57:20.960
 F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
@@ -726,7 +726,7 @@ F src/auth.c 54ab9c6c5803b47c0d45b76ce27eff22a03b4b1f767c5945a3a4eb13aa4c78dc
 F src/backup.c 5c97e8023aab1ce14a42387eb3ae00ba5a0644569e3476f38661fa6f824c3523
 F src/bitvec.c 782cc29b42b47e7ec6348eb0aaf9ffe60063f498387e7249f458d445af4b53e9
 F src/btmutex.c 30dada73a819a1ef5b7583786370dce1842e12e1ad941e4d05ac29695528daea
-F src/btree.c 00fcee37947641f48d4b529d96143e74d056b7afa8f26d61292c90ee59c056b2
+F src/btree.c da98489a981c347cc3a3982ea2810bbb583511a73cc34762547f30dbb4cda7f0
 F src/btree.h 18e5e7b2124c23426a283523e5f31a4bff029131b795bb82391f9d2f3136fc50
 F src/btreeInt.h 9c0f9ea5c9b5f4dcaea18111d43efe95f2ac276cd86d770dce10fd99ccc93886
 F src/build.c 67c1db4c5e89a8519fe9b6dafc287f6bc3627696b5b8536dc5e06db570d8c05f
@@ -770,10 +770,10 @@ F src/os.h 1ff5ae51d339d0e30d8a9d814f4b8f8e448169304d83a7ed9db66a65732f3e63
 F src/os_common.h 6c0eb8dd40ef3e12fe585a13e709710267a258e2c8dd1c40b1948a1d14582e06
 F src/os_kv.c 4d39e1f1c180b11162c6dc4aa8ad34053873a639bac6baae23272fc03349986a
 F src/os_setup.h 6011ad7af5db4e05155f385eb3a9b4470688de6f65d6166b8956e58a3d872107
-F src/os_unix.c 410185df4900817c218c0efdb8064b3481af88cb3f7cea7392f820b6eebc7889
-F src/os_win.c b39f31fb0b137d67091d21880f0fded6b1c3c8c59b9e24e42844a1c0070437d4
+F src/os_unix.c 04e054ab86d86a7be99ebe5265922687791a40df5afc781d059beb47f4a40acd
+F src/os_win.c b8d3cfdf2f40e2f9715b7d8df64f3c0c7ee18743a2dd0c4fc70c1d57fa1aadc7
 F src/os_win.h 4c247cdb6d407c75186c94a1e84d5a22cbae4adcec93fcae8d2bc1f956fd1f19
-F src/pager.c 9fbb541b46125dfa8914827575e6bb4d15048caa008073b1709112d495d7983b
+F src/pager.c 23c0f17deb892da6b32fef1f465507df7ab5cd01d774288cb43695658a649259
 F src/pager.h 6137149346e6c8a3ddc1eeb40aee46381e9bc8b0fcc6dda8a1efde993c2275b8
 F src/parse.y e426d7323311554c75b0aebc426d0fe3c88d9777ffefed236f343ad9e661dc4c
 F src/pcache.c 588cc3c5ccaaadde689ed35ce5c5c891a1f7b1f4d1f56f6cf0143b74d8ee6484
@@ -866,7 +866,7 @@ F src/vdbetrace.c fe0bc29ebd4e02c8bc5c1945f1d2e6be5927ec12c06d89b03ef2a4def34bf8
 F src/vdbevtab.c fc46b9cbd759dc013f0b3724549cc0d71379183c667df3a5988f7e2f1bd485f3
 F src/vtab.c 828221bdbeaaa6d62126ee6d07fd4ec0d09dcaea846f87ad01944d8b7e548859
 F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
-F src/wal.c bcf40795a09b699ad7e42624dd6282b13335164fbabcd5a98a717758cebef451
+F src/wal.c 78da33510b8cb1b096fb5ae9954be910998d2477ddb623e56f5df84e56b5814d
 F src/wal.h ba252daaa94f889f4b2c17c027e823d9be47ce39da1d3799886bbd51f0490452
 F src/walker.c d5006d6b005e4ea7302ad390957a8d41ed83faa177e412f89bc5600a7462a014
 F src/where.c 45a3b496248a0b36d91ce34da3278d54f8fa20e9d3fbd36d45a42051d1118137
@@ -1664,7 +1664,7 @@ F test/skipscan5.test 0672103fd2c8f96bd114133f356192b35ece45c794fe3677e1d9e5e310
 F test/skipscan6.test e2b256cf5d538a605beb97dc97ca5e2836dfc24c5e1d9b7a09e13c069a3b8b49
 F test/snapshot.test a504f2e7009f512ef66c719f0ea1c55a556bdaf1e1312c80a04d46fc1a3e9632
 F test/snapshot2.test 8d6ff5dd9cc503f6e12d408a30409c3f9c653507b24408d9cd7195931c89bc54
-F test/snapshot3.test 41350216abc6c7da37113ad462259c070786e5ad70bdc8709daaed148b1b3a2c
+F test/snapshot3.test 2e0328ba019aa981848e10aded4d7dcd6094ec1f9c6290a34ab18415be0c44eb
 F test/snapshot4.test d4e9347ef2fcabc491fc893506c7bbaf334da3be111d6eb4f3a97cc623b78322
 F test/snapshot_fault.test 129234ceb9b26a0e1000e8563a16e790f5c1412354e70749cbd78c3d5d07d60a
 F test/snapshot_up.test 77dc7853bfb2b4fa249f76e1714cfa1e596826165d9ef22c06ac3a0b7b778d9a
@@ -2036,6 +2036,8 @@ F test/walseh1.test bae700eb99519b6d5cd3f893c04759accc5a59c391d4189fe4dd6995a533
 F test/walsetlk.test 9079cd8ef82570b8cf0067f31e049a72bec353fb2d5f0cc88f1736dc42ba9704
 F test/walsetlk2.test 9097083633cdf55bf1098b694fb8651d0356d38fef28b869481d18029d7ceaf4
 F test/walsetlk3.test 1b82bd92dea7e58f498b4399b0b3d26773dd8ac5c74205ce4a23c207cb8e85fe
+F test/walsetlk_recover.test adccbffc59e365063a4efd2da6b661ae2fcf15d775b6719fe46acd87face08ff
+F test/walsetlk_snapshot.test 86d5588380f9927d8fcbbd75133b0a34fddf959378d6823c6f164a390123f70a
 F test/walshared.test 42e3808582504878af237ea02c42ca793e8a0efaa19df7df26ac573370dbc7a3
 F test/walslow.test 0c51843836c9dcf40a5ac05aa781bfb977b396ee2c872d92bd48b79d5dd9aa23
 F test/walthread.test 14b20fcfa6ae152f5d8e12f5dc8a8a724b7ef189f5d8ef1e2ceab79f2af51747
@@ -2207,9 +2209,9 @@ F tool/version-info.c 3b36468a90faf1bbd59c65fd0eb66522d9f941eedd364fabccd7227350
 F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee87c1b31a7
 F tool/warnings.sh 1ad0169b022b280bcaaf94a7fa231591be96b514230ab5c98fbf15cd7df842dd
 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
-P a7eb03e26345abb450f15f0e25eb843bee1320837d41d272ec1aada8b5bf7ab5
-Q +bf7be67e3fb7b75fc281997f7bf9aa69eaf4da6bdf2fefe359b12d25ec95f512
-R 52f90b034f01ad9dd1968d896e9c6ce2
-U stephan
-Z 7ce4d2874386e874e24917f2f9025685
+P 65c5e66af77a233c6255cb9fca5887269bb38566d3c5482569b8697c72e355ae
+Q +7f9c0cdd0630a41db359b188b226a1ad6a3bae1663c27169acfe25edc7fb171b
+R 0d812b70b020867d9ada415d63c65888
+U drh
+Z 689cb8e844bd2f2b69f9acf239c1ccad
 # Remove this line to create a well-formed Fossil manifest.
index 01c0a76b4c11f595eaf5ceb9bd05ad1fdeb339ae..9c7ea5c54d6fc5e8c61be4ff3c0826ae192c74b4 100644 (file)
@@ -1 +1 @@
-65c5e66af77a233c6255cb9fca5887269bb38566d3c5482569b8697c72e355ae
+8ac4525a2e3100bb5b9460cc49bb64f007911180d5f51461282b1de0201328b6
index 1bd59a1b1fbc173298836772ac49e90409ec7531..25a9b1b4aee603a2d03ff27b01faf0f8ca0577bb 100644 (file)
@@ -3697,6 +3697,13 @@ static SQLITE_NOINLINE int btreeBeginTrans(
       (void)sqlite3PagerWalWriteLock(pPager, 0);
       unlockBtreeIfUnused(pBt);
     }
+#if defined(SQLITE_ENABLE_SETLK_TIMEOUT)
+    if( rc==SQLITE_BUSY_TIMEOUT ){
+      /* If a blocking lock timed out, break out of the loop here so that
+      ** the busy-handler is not invoked.  */
+      break;
+    }
+#endif
   }while( (rc&0xFF)==SQLITE_BUSY && pBt->inTransaction==TRANS_NONE &&
           btreeInvokeBusyHandler(pBt) );
   sqlite3PagerWalDb(pPager, 0);
index 1146545feace013cc3c8ee4a2cbdc562e9d80986..784bc35176793090fa345ddadd694b74f796712c 100644 (file)
@@ -5035,21 +5035,20 @@ static int unixShmLock(
   /* 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. Write lock (ofst==0).
-  **   3. Read locks (ofst>=3 && ofst<SQLITE_SHM_NLOCK).
+  **   1. Recovery lock (ofst==2).
+  **   2. Checkpointer lock (ofst==1).
+  **   3. Write lock (ofst==0).
+  **   4. Read locks (ofst>=3 && ofst<SQLITE_SHM_NLOCK).
   **
   ** 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.
-  **
-  ** It is not permitted to block on the RECOVER lock.
   */
 #if defined(SQLITE_ENABLE_SETLK_TIMEOUT) && defined(SQLITE_DEBUG)
   {
     u16 lockMask = (p->exclMask|p->sharedMask);
     assert( (flags & SQLITE_SHM_UNLOCK) || pDbFd->iBusyTimeout==0 || (
-          (ofst!=2)                                   /* not RECOVER */
+          (ofst!=2 || lockMask==0)
        && (ofst!=1 || lockMask==0 || lockMask==2)
        && (ofst!=0 || lockMask<3)
        && (ofst<3  || lockMask<(1<<ofst))
index cd7e49190a5453ff0adabfbb46c056dc779a1f8a..c7c923e7744241023cfef7ea1d0e0f7008bf6ab7 100644 (file)
@@ -2722,7 +2722,11 @@ static int winHandleLockTimeout(
       if( res==WAIT_OBJECT_0 ){
         ret = TRUE;
       }else if( res==WAIT_TIMEOUT ){
+#if SQLITE_ENABLE_SETLK_TIMEOUT==1
         rc = SQLITE_BUSY_TIMEOUT;
+#else
+        rc = SQLITE_BUSY;
+#endif
       }else{
         /* Some other error has occurred */
         rc = SQLITE_IOERR_LOCK;
@@ -4533,21 +4537,20 @@ static int winShmLock(
   /* 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. Write lock (ofst==0).
-  **   3. Read locks (ofst>=3 && ofst<SQLITE_SHM_NLOCK).
+  **   1. Recovery lock (ofst==2).
+  **   2. Checkpointer lock (ofst==1).
+  **   3. Write lock (ofst==0).
+  **   4. Read locks (ofst>=3 && ofst<SQLITE_SHM_NLOCK).
   **
   ** 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.
-  **
-  ** It is not permitted to block on the RECOVER lock.
   */
 #if defined(SQLITE_ENABLE_SETLK_TIMEOUT) && defined(SQLITE_DEBUG)
   {
     u16 lockMask = (p->exclMask|p->sharedMask);
     assert( (flags & SQLITE_SHM_UNLOCK) || pDbFd->iBusyTimeout==0 || (
-          (ofst!=2)                                   /* not RECOVER */
+          (ofst!=2 || lockMask==0)
        && (ofst!=1 || lockMask==0 || lockMask==2)
        && (ofst!=0 || lockMask<3)
        && (ofst<3  || lockMask<(1<<ofst))
index 21f3ac5f6765f4c1f8d7c48cda37e1fc2d2eaf80..1850ba37bee6e4553aefc8ce6a192b89602da8f6 100644 (file)
@@ -700,6 +700,9 @@ struct Pager {
   Wal *pWal;                  /* Write-ahead log used by "journal_mode=wal" */
   char *zWal;                 /* File name for write-ahead log */
 #endif
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+  sqlite3 *dbWal;
+#endif
 };
 
 /*
@@ -7581,6 +7584,11 @@ static int pagerOpenWal(Pager *pPager){
         pPager->fd, pPager->zWal, pPager->exclusiveMode,
         pPager->journalSizeLimit, &pPager->pWal
     );
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+    if( rc==SQLITE_OK ){
+      sqlite3WalDb(pPager->pWal, pPager->dbWal);
+    }
+#endif
   }
   pagerFixMaplimit(pPager);
 
@@ -7700,6 +7708,7 @@ int sqlite3PagerWalWriteLock(Pager *pPager, int bLock){
 ** blocking locks are required.
 */
 void sqlite3PagerWalDb(Pager *pPager, sqlite3 *db){
+  pPager->dbWal = db;
   if( pagerUseWal(pPager) ){
     sqlite3WalDb(pPager->pWal, db);
   }
index 5fe2296d6c4f74d0ddc5353f510a93f8132925be..fc176988bdeb6014ff1643277906fb58143f9d1b 100644 (file)
--- a/src/wal.c
+++ b/src/wal.c
@@ -3062,7 +3062,6 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int *pCnt){
       rc = walIndexReadHdr(pWal, pChanged);
     }
 #ifdef SQLITE_ENABLE_SETLK_TIMEOUT
-    walDisableBlocking(pWal);
     if( rc==SQLITE_BUSY_TIMEOUT ){
       rc = SQLITE_BUSY;
       *pCnt |= WAL_RETRY_BLOCKED_MASK;
@@ -3077,6 +3076,7 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int *pCnt){
       ** WAL_RETRY this routine will be called again and will probably be
       ** right on the second iteration.
       */
+      walEnableBlocking(pWal);
       if( pWal->apWiData[0]==0 ){
         /* This branch is taken when the xShmMap() method returns SQLITE_BUSY.
         ** We assume this is a transient condition, so return WAL_RETRY. The
@@ -3093,6 +3093,7 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int *pCnt){
         rc = SQLITE_BUSY_RECOVERY;
       }
     }
+    walDisableBlocking(pWal);
     if( rc!=SQLITE_OK ){
       return rc;
     }
index 470d463a6618cd2bbc6c1448f34535fbf44f06a5..6d57b1d0c451a7eb7d8e6bbedad76a77ca3523e8 100644 (file)
@@ -96,6 +96,9 @@ do_test 1.8 {
   list [catch { sqlite3_snapshot_open_blob db3 main $snap } msg] $msg
 } {1 SQLITE_ERROR_SNAPSHOT}
 
+db3 close
+db2 close
+
 #-------------------------------------------------------------------------
 reset_db
 do_execsql_test 2.0 {
diff --git a/test/walsetlk_recover.test b/test/walsetlk_recover.test
new file mode 100644 (file)
index 0000000..1daece7
--- /dev/null
@@ -0,0 +1,104 @@
+# 2025 May 30
+#
+# The author disclaims copyright to this source code.  In place of
+# a legal notice, here is a blessing:
+#
+#    May you do good and not evil.
+#    May you find forgiveness for yourself and forgive others.
+#    May you share freely, never taking more than you give.
+#
+#***********************************************************************
+#
+# TESTRUNNER: slow
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+source $testdir/lock_common.tcl
+set testprefix walsetlk_recover
+
+ifcapable !wal {finish_test ; return }
+# ifcapable !setlk_timeout {finish_test ; return }
+
+do_execsql_test 1.0 {
+  PRAGMA journal_mode = wal;
+  CREATE TABLE t1(a, b);
+  INSERT INTO t1 VALUES(1, 2);
+  INSERT INTO t1 VALUES(3, 4);
+  INSERT INTO t1 VALUES(5, 6);
+} {wal}
+
+db_save_and_close
+db_restore
+
+testfixture_nb myvar {
+
+  testvfs tvfs -fullshm 1
+  sqlite3 db test.db -vfs tvfs
+  tvfs script vfs_callback
+  tvfs filter xRead
+
+  set ::done 0
+  proc vfs_callback {method file args} {
+    if {$::done==0 && [string match *wal $file]} {
+      after 4000
+      set ::done 1
+    }
+    return "SQLITE_OK"
+  }
+
+  db eval {
+    SELECT * FROM t1
+  }
+
+  db close
+}
+
+# Give the [testfixture_nb] command time to start
+after 1000 {set xyz 1}
+vwait xyz
+
+testvfs tvfs -fullshm 1
+sqlite3 db test.db -vfs tvfs
+
+tvfs script sleep_callback
+tvfs filter xSleep
+set ::sleep_count 0
+proc sleep_callback {args} {
+  incr ::sleep_count
+}
+
+sqlite3 db test.db -vfs tvfs
+db timeout 500
+set tm [lindex [time {
+  catch {
+    db eval {SELECT * FROM t1}
+  } msg
+}] 0]
+
+do_test 1.2         { set ::msg } {database is locked}
+do_test 1.3.($::tm) { expr $::tm>400000 && $::tm<2000000 } 1
+
+vwait myvar
+
+do_execsql_test 1.4 {
+  SELECT * FROM t1
+} {1 2 3 4 5 6}
+
+db close
+tvfs delete
+
+# All SQLite builds should pass the tests above. SQLITE_ENABLE_SETLK_TIMEOUT=1
+# builds do so without calling the VFS xSleep method.
+if {$::sqlite_options(setlk_timeout)==1} {
+  do_test 1.5.1 {
+    set ::sleep_count
+  } 0
+} else {
+  do_test 1.5.2 {
+    expr $::sleep_count>0
+  } 1
+}
+
+finish_test
+
diff --git a/test/walsetlk_snapshot.test b/test/walsetlk_snapshot.test
new file mode 100644 (file)
index 0000000..e05ad69
--- /dev/null
@@ -0,0 +1,109 @@
+# 2025 May 30
+#
+# The author disclaims copyright to this source code.  In place of
+# a legal notice, here is a blessing:
+#
+#    May you do good and not evil.
+#    May you find forgiveness for yourself and forgive others.
+#    May you share freely, never taking more than you give.
+#
+#***********************************************************************
+#
+# TESTRUNNER: slow
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+source $testdir/lock_common.tcl
+set testprefix walsetlk_snapshot
+
+ifcapable !wal {finish_test ; return }
+ifcapable !snapshot {finish_test; return}
+
+db close
+testvfs tvfs -fullshm 1
+sqlite3 db test.db -vfs tvfs
+tvfs script sleep_callback
+tvfs filter xSleep
+set ::sleep_count 0
+proc sleep_callback {args} {
+  incr ::sleep_count
+}
+
+do_execsql_test 1.0 {
+  PRAGMA journal_mode = wal;
+  CREATE TABLE t1(a, b);
+  INSERT INTO t1 VALUES(1, 2);
+  INSERT INTO t1 VALUES(3, 4);
+  INSERT INTO t1 VALUES(5, 6);
+} {wal}
+
+do_test 1.1 {
+  db eval BEGIN
+  set ::snap [sqlite3_snapshot_get db main]
+  db eval {
+    INSERT INTO t1 VALUES(7, 8);
+    COMMIT;
+  }
+} {}
+
+testfixture_nb myvar {
+
+  testvfs tvfs -fullshm 1
+  sqlite3 db test.db -vfs tvfs
+  tvfs script vfs_callback
+  tvfs filter {xWrite}
+
+  set ::done 0
+  proc vfs_callback {args} {
+    if {$::done==0} {
+      after 4000
+      set ::done 1
+    }
+    return "SQLITE_OK"
+  }
+
+  db eval {
+    PRAGMA wal_checkpoint;
+  }
+
+  db close
+}
+
+# Give the [testfixture_nb] command time to start
+after 1000 {set xyz 1}
+vwait xyz
+
+db timeout 500
+set tm [lindex [time {
+  catch {
+    db eval BEGIN
+      sqlite3_snapshot_open db main $::snap
+  } msg
+}] 0]
+
+do_test 1.2 { set ::msg } {SQLITE_BUSY}
+do_test 1.3.($::tm) { expr $::tm<2000000 } 1
+
+do_execsql_test 1.4 {
+  SELECT * FROM t1
+} {1 2 3 4 5 6 7 8}
+
+sqlite3_snapshot_free $::snap
+
+vwait myvar
+
+# All SQLite builds should pass the tests above. SQLITE_ENABLE_SETLK_TIMEOUT=1
+# builds do so without calling the VFS xSleep method.
+if {$::sqlite_options(setlk_timeout)==1} {
+  do_test 1.5.1 {
+    set ::sleep_count
+  } 0
+} else {
+  do_test 1.5.2 {
+    expr $::sleep_count>0
+  } 1
+}
+
+finish_test
+