]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Minor cleanups and docs. Teach the OPFS concurrency tester to deal with SQLITE_BUSY...
authorstephan <stephan@noemail.net>
Fri, 6 Mar 2026 19:33:25 +0000 (19:33 +0000)
committerstephan <stephan@noemail.net>
Fri, 6 Mar 2026 19:33:25 +0000 (19:33 +0000)
FossilOrigin-Name: 247ffed141f66a6a5a396a3e002995a9f00c70333271199200530066e77956c4

ext/wasm/api/opfs-common-shared.c-pp.js
ext/wasm/api/sqlite3-opfs-async-proxy.c-pp.js
ext/wasm/api/sqlite3-vfs-opfs.c-pp.js
ext/wasm/tests/opfs/concurrency/worker.js
manifest
manifest.uuid

index 20201841736eaefea5c8f860c14c4affca4d2e20..734b211c09286f5262574ef2563188cd8aad7208 100644 (file)
@@ -588,7 +588,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
        of this value is also used for determining how long to wait on
        lock contention to free up.
     */
-    state.asyncIdleWaitTime = isWebLocker ? 150 : 150;
+    state.asyncIdleWaitTime = isWebLocker ? 250 : 150;
 
     /**
        Whether the async counterpart should log exceptions to
@@ -599,7 +599,8 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
        0 = no exception logging.
 
        1 = only log exceptions for "significant" ops like xOpen(),
-       xRead(), and xWrite().
+       xRead(), and xWrite(). Exceptions related to, e.g., wait/retry
+       loops in acquiring SyncAccessHandles are not logged.
 
        2 = log all exceptions.
     */
@@ -636,29 +637,20 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
     */
     state.opIds = Object.create(null);
     {
-      /*
-        Maintenance reminder:
-
-        Some of these fields are only for use by the "opfs-wl" VFS,
-        but they must also be set up for the "ofps" VFS so that the
-        sizes and offsets calculated here are consistent in the async
-        proxy. Hypothetically they could differ and it would cope
-        but... why invite disaster over eliding a few superfluous (for
-        "opfs') properties?
-      */
       /* Indexes for use in our SharedArrayBuffer... */
       let i = 0;
       /* SAB slot used to communicate which operation is desired
          between both workers. This worker writes to it and the other
-         listens for changes. */
+         listens for changes and clears it. The values written to it
+         are state.opIds.x[A-Z][a-z]+, defined below.*/
       state.opIds.whichOp = i++;
-      /* Slot for storing return values. This worker listens to that
-         slot and the other worker writes to it. */
+      /* Slot for storing return values. This side listens to that
+         slot and the async proxy writes to it. */
       state.opIds.rc = i++;
-      /* Each function gets an ID which this worker writes to
-         the whichOp slot. The async-api worker uses Atomic.wait()
-         on the whichOp slot to figure out which operation to run
-         next. */
+      /* Each function gets an ID which this worker writes to the
+         state.opIds.whichOp slot. The async-api worker uses
+         Atomic.wait() on the whichOp slot to figure out which
+         operation to run next. */
       state.opIds.xAccess = i++;
       state.opIds.xClose = i++;
       state.opIds.xDelete = i++;
@@ -672,25 +664,28 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
       state.opIds.xTruncate = i++;
       state.opIds.xUnlock = i++;
       state.opIds.xWrite = i++;
-      state.opIds.mkdir = i++;
+      state.opIds.mkdir = i++ /*currently unused*/;
       /** Internal signals which are used only during development and
           testing via the dev console. */
       state.opIds['opfs-async-metrics'] = i++;
       state.opIds['opfs-async-shutdown'] = i++;
       /* The retry slot is used by the async part for wait-and-retry
-         semantics. Though we could hypothetically use the xSleep slot
-         for that, doing so might lead to undesired side effects. */
+         semantics. It is never written to, only used as a convenient
+         place to wait-with-timeout for a value which will never be
+         written, i.e. sleep()ing, before retrying a failed attempt to
+         acquire a SharedAccessHandle. */
       state.opIds.retry = i++;
       state.sabOP = new SharedArrayBuffer(
-        i * 4/* ==sizeof int32, noting that Atomics.wait() and friends
-                can only function on Int32Array views of an SAB. */);
+        i * 4/* 4==sizeof int32, noting that Atomics.wait() and
+                friends can only function on Int32Array views of an
+                SAB. */);
     }
     /**
        SQLITE_xxx constants to export to the async worker
        counterpart...
     */
     state.sq3Codes = Object.create(null);
-    [
+    for(const k of [
       'SQLITE_ACCESS_EXISTS',
       'SQLITE_ACCESS_READWRITE',
       'SQLITE_BUSY',
@@ -724,17 +719,16 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
       'SQLITE_LOCK_RESERVED',
       'SQLITE_LOCK_PENDING',
       'SQLITE_LOCK_EXCLUSIVE'
-    ].forEach((k)=>{
-      if(undefined === (state.sq3Codes[k] = capi[k])){
-        toss("Maintenance required: not found:",k);
-      }
-    });
+    ]){
+      state.sq3Codes[k] =
+        capi[k] ?? toss("Maintenance required: not found:",k);
+    }
 
     state.opfsFlags = Object.assign(Object.create(null),{
       /**
          Flag for use with xOpen(). URI flag "opfs-unlock-asap=1"
          enables this. See defaultUnlockAsap, below.
-       */
+      */
       OPFS_UNLOCK_ASAP: 0x01,
       /**
          Flag for use with xOpen(). URI flag "delete-before-open=1"
@@ -747,33 +741,34 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
          downstream errors.  An unlink can fail if, e.g., another tab
          has the handle open.
 
-         It goes without saying that deleting a file out from under another
-         instance results in Undefined Behavior.
+         It goes without saying that deleting a file out from under
+         another instance results in Undefined Behavior.
       */
       OPFS_UNLINK_BEFORE_OPEN: 0x02,
       /**
-         If true, any async routine which implicitly acquires a sync
-         access handle (i.e. an OPFS lock) will release that lock at
-         the end of the call which acquires it. If false, such
-         "autolocks" are not released until the VFS is idle for some
-         brief amount of time.
-
-         The benefit of enabling this is much higher concurrency. The
-         down-side is much-reduced performance (as much as a 4x decrease
-         in speedtest1).
+         If true, any async routine which must implicitly acquire a
+         sync access handle (i.e. an OPFS lock), without an active
+         xLock(), will release that lock at the end of the call which
+         acquires it. If false, such implicit locks are not released
+         until the VFS is idle for some brief amount of time, as
+         defined by state.asyncIdleWaitTime.
+
+         The benefit of enabling this is higher concurrency. The
+         down-side is much-reduced performance (as much as a 4x
+         decrease in speedtest1).
       */
       defaultUnlockAsap: false
     });
 
-    opfsVfs.metrics.reset();
+    opfsVfs.metrics.reset()/*must not be called until state.opIds is set up*/;
     const metrics = opfsVfs.metrics.counters;
 
     /**
        Runs the given operation (by name) in the async worker
        counterpart, waits for its response, and returns the result
-       which the async worker writes to SAB[state.opIds.rc]. The
-       2nd and subsequent arguments must be the arguments for the
-       async op.
+       which the async worker writes to SAB[state.opIds.rc]. The 2nd
+       and subsequent arguments must be the arguments for the async op
+       (see sqlite3-opfs-async-proxy.c-pp.js).
     */
     const opRun = opfsVfs.opRun = (op,...args)=>{
       const opNdx = state.opIds[op] || toss("Invalid op ID:",op);
@@ -791,14 +786,15 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
           https://github.com/sqlite/sqlite-wasm/issues/12
 
           Summary: in at least one browser flavor, under high loads,
-          the wait()/notify() pairings can get out of sync. Calling
-          wait() here until it returns 'not-equal' gets them back in
-          sync.
+          the wait()/notify() pairings can get out of sync and/or
+          spuriously wake up. Calling wait() here until it returns
+          'not-equal' gets them back in sync.
         */
       }
       /* When the above wait() call returns 'not-equal', the async
-         half will have completed the operation and reported its results
-         in the state.opIds.rc slot of the SAB. */
+         half will have completed the operation and reported its
+         results in the state.opIds.rc slot of the SAB. It may have
+         also serialized an exception for us. */
       const rc = Atomics.load(state.sabOPView, state.opIds.rc);
       metrics[op].wait += performance.now() - t;
       if(rc && state.asyncS11nExceptions){
index e2b48bff575c579384248a439f37557c366e2270..e83cdf416dd7c4d85352a5f0d25d4aefa1c4d82a 100644 (file)
@@ -618,7 +618,6 @@ const installAsyncProxy = function(){
                                          isFromUnlock/*only if called from this.xUnlock()*/){
       const whichOp = isFromUnlock ? 'xUnlock' : 'xLock';
       const fh = __openFiles[fid];
-      const lockName = "sqlite3-vfs-opfs:" + fh.filenameAbs;
       //error("xLock()",fid, lockType, isFromUnlock, fh);
       const requestedMode = (lockType >= state.sq3Codes.SQLITE_LOCK_RESERVED)
             ? 'exclusive' : 'shared';
@@ -627,38 +626,48 @@ const installAsyncProxy = function(){
         if( existing.mode === requestedMode
             || (existing.mode === 'exclusive'
                 && requestedMode === 'shared') ) {
-          storeAndNotify(whichOp, 0);
-          existing.mode = requestedMode/* ??? */;
           fh.xLock = lockType;
+          storeAndNotify(whichOp, 0);
+          /* Don't do this: existing.mode = requestedMode;
+
+             Paraphrased from advice given by a consultanting
+             developer:
+
+             If you hold an exclusive lock and SQLite requests shared,
+             you should keep exiting.mode as exclusive in because the
+             underlying Web Lock is still exclusive. Changing it to
+             shared would trick xLock into thinking it needs to
+             perform a release/re-acquire dance if an exclusive is
+             later requested.
+          */
           return 0 /* Already held at required or higher level */;
         }
         /*
           Upgrade path: we must release shared and acquire exclusive.
           This transition is NOT atomic in Web Locks API.
+
+          Except that it _effectively_ is atomic if we don't call
+          closeSyncHandle(fh), as no other worker can lock that
+          until we let it go. But we can't do that without leading
+          to a deadly embrace, so...
         */
-        if( 1 ){
-          /* Except that it _effectively_ is atomic if we don't call
-             closeSyncHandle(fh), as no other worker can lock that
-             until we let it go. But we can't do that without leading
-             to a deadly embrace, so... */
-          await closeSyncHandle(fh);
-        }
+        await closeSyncHandle(fh);
         existing.resolveRelease();
         delete __activeWebLocks[fid];
       }
 
+      const lockName = "sqlite3-vfs-opfs:" + fh.filenameAbs;
       const oldLockType = fh.xLock;
       return new Promise((resolveWaitLoop) => {
         //error("xLock() initial promise entered...");
         navigator.locks.request(lockName, { mode: requestedMode }, async (lock) => {
           //error("xLock() Web Lock entered.", fh);
-          fh.xLock = lockType/*must be set before getSyncHandle() is called*/;
+          fh.xLock = lockType/*must be set before getSyncHandle() is called!*/;
           __implicitLocks.delete(fid);
           let rc = 0;
           try{
-            /* Make ONE attempt to get the handle, but with a
-               higher-than-default retry-wait. */
-            await getSyncHandle(fh, 'xLock', 317, 5);
+            /* Make only one attempt to get the handle. */
+            await getSyncHandle(fh, 'xLock');
           }catch(e){
             fh.xLock = oldLockType;
             state.s11n.storeException(1, e);
@@ -669,16 +678,19 @@ const installAsyncProxy = function(){
                 : new Promise((resolveRelease) => {
                   __activeWebLocks[fid] = { mode: requestedMode, resolveRelease };
                 });
-          storeAndNotify(whichOp, rc) /* Unblock the C side */;
-          resolveWaitLoop(0) /* Unblock waitLoop() */;
-          await releasePromise; // Hold the lock until xUnlock
+          storeAndNotify(whichOp, rc) /* unblock the C side */;
+          resolveWaitLoop(0) /* unblock waitLoop() */;
+          await releasePromise /* hold the lock until xUnlock */;
         });
       });
     };
 
+    /** Internal helper for the opfs-wl xUnlock() */
     const wlCloseHandle = async(fh)=>{
       let rc = 0;
       try{
+        /* For the record, we've never once seen closeSyncHandle()
+           throw, nor should it because destructors do not throw. */
         await closeSyncHandle(fh);
       }catch(e){
         state.s11n.storeException(1,e);
@@ -696,7 +708,7 @@ const installAsyncProxy = function(){
         storeAndNotify('xUnlock', rc);
         return rc;
       }
-      error("xUnlock()",fid, lockType, fh);
+      //error("xUnlock()",fid, lockType, fh);
       let rc = 0;
       if( lockType === state.sq3Codes.SQLITE_LOCK_NONE ){
         /* SQLite usually unlocks all the way to NONE */
@@ -723,8 +735,8 @@ const installAsyncProxy = function(){
     }
 
   }else{
-
     /* Original/"legacy" xLock() and xUnlock() */
+
     vfsAsyncImpls.xLock = async function(fid/*sqlite3_file pointer*/,
                                          lockType/*SQLITE_LOCK_...*/){
       const fh = __openFiles[fid];
index 6d0988d6d71a81f89a9fb975b472ac8319c32900..ebe74e38b6182f1bf09ac47dba0a83d41c7a2c0d 100644 (file)
@@ -90,6 +90,7 @@ const installOpfsVfs = async function callee(options){
         mTimeEnd = opfsVfs.mTimeEnd,
         opRun = opfsVfs.opRun,
         debug = (...args)=>sqlite3.config.debug("opfs:",...args),
+        warn = (...args)=>sqlite3.config.warn("opfs:",...args),
         __openFiles = opfsVfs.__openFiles;
 
   //debug("options:",JSON.stringify(options));
@@ -99,11 +100,14 @@ const installOpfsVfs = async function callee(options){
   return opfsVfs.bindVfs(util.nu({
     xLock: function(pFile,lockType){
       mTimeStart('xLock');
-      debug("xLock()...");
+      //debug("xLock()...");
       const f = __openFiles[pFile];
       const rc = opRun('xLock', pFile, lockType);
-      debug("xLock() rc ",rc);
-      if( 0===rc ) f.lockType = lockType;
+      if( rc ){
+        warn("xLock() rc ",rc);
+      }else{
+        f.lockType = lockType;
+      }
       mTimeEnd();
       return rc;
     },
index 365900c2d5d8c2a0f7a0775f97aa32974b630d1d..0f9c9b68cb42b017b6d3791c70f35bdd07f46559 100644 (file)
@@ -61,17 +61,31 @@ globalThis.sqlite3InitModule().then(async function(sqlite3){
       stderr("Invalid VFS name:",vfs);
       return;
     }
-    db = new ctor({
-      filename: 'file:'+dbName+'?opfs-unlock-asap='+options.unlockAsap,
-      flags: 'c'
-    });
-    sqlite3.capi.sqlite3_busy_timeout(db.pointer, 5000);
-    db.transaction((db)=>{
-      db.exec([
-        "create table if not exists t1(w TEXT UNIQUE ON CONFLICT REPLACE,v);",
-        "create table if not exists t2(w TEXT UNIQUE ON CONFLICT REPLACE,v);"
-      ]);
-    });
+    while(true){
+      try{
+        if( !db ){
+          db = new ctor({
+            filename: 'file:'+dbName+'?opfs-unlock-asap='+options.unlockAsap,
+              flags: 'c'
+          });
+          sqlite3.capi.sqlite3_busy_timeout(db.pointer, 15000);
+        }
+        db.transaction((db)=>{
+          db.exec([
+            "create table if not exists t1(w TEXT UNIQUE ON CONFLICT REPLACE,v);",
+            "create table if not exists t2(w TEXT UNIQUE ON CONFLICT REPLACE,v);"
+          ]);
+        });
+        break;
+      }catch(e){
+        if(e instanceof sqlite3.SQLite3Error
+           && sqlite3.capi.SQLITE_BUSY===e.resultCode){
+          stderr("Retrying for BUSY: ",e.message);
+          continue;
+        }
+        throw e;
+      }
+    }
 
     const maxIterations =
           urlArgs.has('iterations') ? (+urlArgs.get('iterations') || 10) : 10;
@@ -81,14 +95,24 @@ globalThis.sqlite3InitModule().then(async function(sqlite3){
       ++interval.count;
       const prefix = "v(#"+interval.count+")";
       stdout("Setting",prefix,"=",tm);
-      try{
-        db.exec({
-          sql:"INSERT OR REPLACE INTO t1(w,v) VALUES(?,?)",
-          bind: [options.workerName, new Date().getTime()]
-        });
-        //stdout("Set",prefix);
-      }catch(e){
-        interval.error = e;
+      while(true){
+        try{
+          db.exec({
+            sql:"INSERT OR REPLACE INTO t1(w,v) VALUES(?,?)",
+            bind: [options.workerName, new Date().getTime()]
+          });
+          //stdout("Set",prefix);
+          break;
+        }catch(e){
+          if(e instanceof sqlite3.SQLite3Error
+             && sqlite3.capi.SQLITE_BUSY===e.resultCode){
+            stderr("Retrying for BUSY: ",e.message);
+            continue;
+          }
+          stderr("Error: ",e.message);
+          interval.error = e;
+          throw e;
+        }
       }
       //stdout("doWork()",prefix,"error ",interval.error);
     };
index d85a70c59103416f69989a720a4f034d6182a1d0..0974a04f21219d7890ce310834de47fdde8facdc 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C This\sone\sreliably\sruns\s5\sworkers.\sChecking\sin\sbefore\ssubsequent\scleanups\sand\sdebug\soutput\sremoval\sbreak\sit.
-D 2026-03-06T17:10:28.455
+C Minor\scleanups\sand\sdocs.\sTeach\sthe\sOPFS\sconcurrency\stester\sto\sdeal\swith\sSQLITE_BUSY\sinstead\sof\sfailing.
+D 2026-03-06T19:33:25.647
 F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
@@ -585,7 +585,7 @@ F ext/wasm/api/README.md a905d5c6bfc3e2df875bd391d6d6b7b48d41b43bdee02ad115b4724
 F ext/wasm/api/extern-post-js.c-pp.js d9f42ecbedc784c0d086bc37800e52946a14f7a21600b291daa3f963c314f930
 F ext/wasm/api/extern-pre-js.js cc61c09c7a24a07dbecb4c352453c3985170cec12b4e7e7e7a4d11d43c5c8f41
 F ext/wasm/api/opfs-common-inline.c-pp.js 5be8d6d91963849e218221b48206ae55612630bb2cd7f30b1b6fcf7a9e374b76
-F ext/wasm/api/opfs-common-shared.c-pp.js 7bfbf3a5ce1b558ec3d0b3e14f375e9f6003b6bb49c58480e2fbf2726cf59b2c
+F ext/wasm/api/opfs-common-shared.c-pp.js eccb37a2347b8b17a664401cd8ef0ee0a7e18cb81939ee4ef404905e8e9188bf
 F ext/wasm/api/post-js-footer.js a50c1a2c4d008aede7b2aa1f18891a7ee71437c2f415b8aeb3db237ddce2935b
 F ext/wasm/api/post-js-header.js f35d2dcf1ab7f22a93d565f8e0b622a2934fc4e743edf3b708e4dd8140eeff55
 F ext/wasm/api/pre-js.c-pp.js 9234ea680a2f6a2a177e8dcd934bdc5811a9f8409165433a252b87f4c07bba6f
@@ -594,12 +594,12 @@ F ext/wasm/api/sqlite3-api-oo1.c-pp.js 45454631265d9ce82685f1a64e1650ee19c8e121c
 F ext/wasm/api/sqlite3-api-prologue.js 98fedc159c9239b226d19567d7172300dee5ffce176e5fa2f62dd1f17d088385
 F ext/wasm/api/sqlite3-api-worker1.c-pp.js 1041dd645e8e821c082b628cd8d9acf70c667430f9d45167569633ffc7567938
 F ext/wasm/api/sqlite3-license-version-header.js 98d90255a12d02214db634e041c8e7f2f133d9361a8ebf000ba9c9af4c6761cc
-F ext/wasm/api/sqlite3-opfs-async-proxy.c-pp.js b3235922c15ee9b92a5424e34580bf16cb971adf23559c9e7119d563b8da2fe9
+F ext/wasm/api/sqlite3-opfs-async-proxy.c-pp.js c19ca5986bceb60561973635bd68acbb93f5e1752b1d1b7f4cae20abaa8d5bd1
 F ext/wasm/api/sqlite3-vfs-helper.c-pp.js 3f828cc66758acb40e9c5b4dcfd87fd478a14c8fb7f0630264e6c7fa0e57515d
 F ext/wasm/api/sqlite3-vfs-kvvfs.c-pp.js a61dd2b4d919d2d5d83c5c7e49b89ecbff2525ff81419f6a6dbaecaf3819c490
 F ext/wasm/api/sqlite3-vfs-opfs-sahpool.c-pp.js 1575ea6bbcf2da1e6df6892c17521a0c1c1c199a672e9090176ea0b88de48bd9
 F ext/wasm/api/sqlite3-vfs-opfs-wl.c-pp.js 8233c5f9021b0213134e2adbaf6036b8f1dffd4747083a4087c1c19ae107f962
-F ext/wasm/api/sqlite3-vfs-opfs.c-pp.js 3fde62ac67c963ee04030cf279357bb19b98a420973f55245c44396828b582d6
+F ext/wasm/api/sqlite3-vfs-opfs.c-pp.js f3bef4dbb8364a37471e4bc33e9b1e52795596456090007aaeae25acc35d2e85
 F ext/wasm/api/sqlite3-vtab-helper.c-pp.js 366596d8ff73d4cefb938bbe95bc839d503c3fab6c8335ce4bf52f0d8a7dee81
 F ext/wasm/api/sqlite3-wasm.c 45bb20e19b245136711f9b78584371233975811b6560c29ed9b650e225417e29
 F ext/wasm/api/sqlite3-worker1-promiser.c-pp.js aa9715f661fb700459a5a6cb1c32a4d6a770723b47aa9ac0e16c2cf87d622a66
@@ -647,7 +647,7 @@ F ext/wasm/tester1.c-pp.html 52d88fe2c6f21a046030a36410b4839b632f4424028197a45a3
 F ext/wasm/tester1.c-pp.js a4e79fbf63bb3255d2b8ffc1cd538c115d2f6b599bc324904c80f6644379a284
 F ext/wasm/tests/opfs/concurrency/index.html 657578a6e9ce1e9b8be951549ed93a6a471f4520a99e5b545928668f4285fb5e
 F ext/wasm/tests/opfs/concurrency/test.js 74f4ef9a827d081e6bb0ffb1d124bb54015dab8f7ae47abd5b5f26d71633331a
-F ext/wasm/tests/opfs/concurrency/worker.js d0303b1403867e97455f7563285af3eb4471961b19bc22e45d021d896d48e27c
+F ext/wasm/tests/opfs/concurrency/worker.js ce1d5d7545b17f62bac2dcce2505a89c3690e1d9209512cc51512cee6e3024f5
 F ext/wasm/tests/opfs/sahpool/digest-worker.js b0ab6218588f1f0a6d15a363b493ceaf29bfb87804d9e0165915a9996377cf79
 F ext/wasm/tests/opfs/sahpool/digest.html 206d08a34dc8bd570b2581d3d9ab3ecad3201b516a598dd096dcf3cf8cd81df8
 F ext/wasm/tests/opfs/sahpool/index.html be736567fd92d3ecb9754c145755037cbbd2bca01385e2732294b53f4c842328
@@ -2191,8 +2191,8 @@ F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee
 F tool/warnings.sh d924598cf2f55a4ecbc2aeb055c10bd5f48114793e7ba25f9585435da29e7e98
 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
 F tool/winmain.c 00c8fb88e365c9017db14c73d3c78af62194d9644feaf60e220ab0f411f3604c
-P 53aa080e357d7a2ffeab68a3584fda43d51ecef3dc8a1d46dd32392ae4f9740c
-R fcfb0bfb06d58bd7f0d57908763a15cf
+P ba81d95febc5fd0f9bbb2685fef5b1b10f9991751f2bdfafba80c15877af1cef
+R 5bd89f58e70b2c6dcff7610e01717205
 U stephan
-Z 62318d6c194878a798365e388df66b6b
+Z 52bcc9f2059e4acc631f2cd7a59885ba
 # Remove this line to create a well-formed Fossil manifest.
index fc702daf4565456154fe452e8fb5a0e80c43e9dc..e3c556d7b322d62b69acede1e1da3294b2a1928e 100644 (file)
@@ -1 +1 @@
-ba81d95febc5fd0f9bbb2685fef5b1b10f9991751f2bdfafba80c15877af1cef
+247ffed141f66a6a5a396a3e002995a9f00c70333271199200530066e77956c4