From: stephan Date: Sat, 7 Mar 2026 01:01:13 +0000 (+0000) Subject: For backwards compatibility, ensure that the "opfs" VFS does not specifically require... X-Git-Tag: major-release~100^2~7 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=57d0fe254a9628776eb081750543c8ce1c62fc83;p=thirdparty%2Fsqlite.git For backwards compatibility, ensure that the "opfs" VFS does not specifically require Atomics.waitAsync() (a new requirement of "opfs-wl"), but make use of it if available. Only apply jitter to the concurrency test runs at random intervals. FossilOrigin-Name: f2175f526c00cfe562e8f332eb197b5ef2c3d6be1fff2aab1566c2c533a293ac --- diff --git a/ext/wasm/api/sqlite3-opfs-async-proxy.c-pp.js b/ext/wasm/api/sqlite3-opfs-async-proxy.c-pp.js index f89f73c311..6362c6efd4 100644 --- a/ext/wasm/api/sqlite3-opfs-async-proxy.c-pp.js +++ b/ext/wasm/api/sqlite3-opfs-async-proxy.c-pp.js @@ -668,8 +668,7 @@ const installAsyncProxy = function(){ __implicitLocks.delete(fid); let rc = 0; try{ - /* Make only one attempt to get the handle. */ - await getSyncHandle(fh, 'xLock'); + await getSyncHandle(fh, 'xLock', state.asyncIdleWaitTime, 5); }catch(e){ fh.xLock = oldLockType; state.s11n.storeException(1, e); @@ -794,15 +793,64 @@ const installAsyncProxy = function(){ const opView = state.sabOPView; const slotWhichOp = opIds.whichOp; const idleWaitTime = state.asyncIdleWaitTime; + const hasWaitAsync = !!Atomics.waitAsync; while(!flagAsyncShutdown){ try { - const opId = Atomics.load(opView, slotWhichOp); - if( 0===opId ){ - const rv = Atomics.waitAsync(opView, slotWhichOp, 0, - idleWaitTime); - if( rv.async ) await rv.value; - await releaseImplicitLocks(); - continue; + let opId; + if( hasWaitAsync ){ + opId = Atomics.load(opView, slotWhichOp); + if( 0===opId ){ + const rv = Atomics.waitAsync(opView, slotWhichOp, 0, + idleWaitTime); + if( rv.async ) await rv.value; + await releaseImplicitLocks(); + continue; + } + }else{ + /** + For browsers without Atomics.waitAsync(), we require + the legacy implementation. Browser versions where + waitAsync() arrived: + + Chrome: 90 (2021-04-13) + Firefox: 145 (2025-11-11) + Safari: 16.4 (2023-03-27) + + The "opfs" VFS was not born until Chrome was somewhere in + the v104-108 range and did not work with Safari < v17 + (2023-09-18) due to a WebKit bug which restricted OPFS + access from sub-Workers. + + The waitAsync() counterpart of this block has shown to be + slightly more performant for the "opfs" VFS than this + block (whereas "opfs-wl" _requires_ that block), so we + enable it where it's available, regardless of the value + of isWebLocker, with the note that if isWebLocker is true + then the bootstrapping of this script will have failed if + waitAsync() is not available. + */ + if('not-equal'!==Atomics.wait( + state.sabOPView, slotWhichOp, 0, state.asyncIdleWaitTime + )){ + /* Maintenance note: we compare against 'not-equal' because + + https://github.com/tomayac/sqlite-wasm/issues/12 + + is reporting that this occasionally, under high loads, + returns 'ok', which leads to the whichOp being 0 (which + isn't a valid operation ID and leads to an exception, + along with a corresponding ugly console log + message). Unfortunately, the conditions for that cannot + be reliably reproduced. The only place in our code which + writes a 0 to the state.opIds.whichOp SharedArrayBuffer + index is a few lines down from here, and that instance + is required in order for clear communication between + the sync half of this proxy and this half. + */ + await releaseImplicitLocks(); + continue; + } + opId = Atomics.load(state.sabOPView, slotWhichOp); } Atomics.store(opView, slotWhichOp, 0); const hnd = f.opHandlers[opId]?.f ?? toss("No waitLoop handler for whichOp #",opId); @@ -812,6 +860,16 @@ const installAsyncProxy = function(){ operation */ ) || []; //error("waitLoop() whichOp =",opId, f.opHandlers[opId].key, args); +//#if nope + if( isWebLocker && (opId==opIds.xLock || opIds==opIds.xUnlock) ){ + /* An expert suggests that this introduces a race condition, + but my eyes aren't seeing it. The hope was that this + would improve the lock speed a tick, but it does not + appear to. */ + hnd(...args); + continue; + } +//#endif await hnd(...args); }catch(e){ error('in waitLoop():', e); @@ -865,7 +923,7 @@ if(globalThis.window === globalThis){ }else if(!globalThis.Atomics){ wPost('opfs-unavailable', "Missing Atomics API.", "The server must emit the COOP/COEP response headers to enable that."); -}else if('opfs-wl'===vfsName && !globalThis.Atomics.waitAsync){ +}else if(isWebLocker && !globalThis.Atomics.waitAsync){ wPost('opfs-unavailable',"Missing required Atomics.waitSync() for "+vfsName); }else if(!globalThis.FileSystemHandle || !globalThis.FileSystemDirectoryHandle || diff --git a/ext/wasm/tests/opfs/concurrency/index.html b/ext/wasm/tests/opfs/concurrency/index.html index 54ed04a4f6..c5e929b010 100644 --- a/ext/wasm/tests/opfs/concurrency/index.html +++ b/ext/wasm/tests/opfs/concurrency/index.html @@ -25,15 +25,14 @@ the workers=N URL flag. Set the time between each workload with interval=N (milliseconds). Set the number of worker iterations with iterations=N. - Enable OPFS VFS verbosity with verbose=1-3 (output - goes to the dev console). Disable/enable "unlock ASAP" mode - (higher concurrency, lower speed) with unlock-asap=0-1. + Disable/enable "unlock ASAP" mode (potentially higher + concurrency but lower speed) with unlock-asap=0-1.

Achtung: if it does not start to do anything within a couple of seconds, check the dev console: Chrome sometimes fails to load - the wasm module due to "cannot allocate WasmMemory." Closing and - re-opening the tab usually resolves it, but sometimes restarting - the browser is required. + the wasm module due to "cannot allocate WasmMemory" when + reloading the page. Closing and re-opening the tab usually + resolves it, but sometimes restarting the browser is required.

Links for various testing options:

diff --git a/ext/wasm/tests/opfs/concurrency/test.js b/ext/wasm/tests/opfs/concurrency/test.js index bdc15a3c4b..f8d7f10399 100644 --- a/ext/wasm/tests/opfs/concurrency/test.js +++ b/ext/wasm/tests/opfs/concurrency/test.js @@ -139,12 +139,13 @@ return li.join('&'); }; for(const opt of [ - {interval: 500, workers: 1, iterations: 5, vfs: 'opfs-wl', opfsVerbose: 2}, - {interval: 500, workers: 2, iterations: 5, vfs: 'opfs-wl', opfsVerbose: 2}, {interval: 1000, workers: 5, iterations: 30}, {interval: 500, workers: 5, iterations: 30}, {interval: 250, workers: 3, iterations: 30}, - {interval: 600, workers: 5, iterations: 100} + {interval: 600, workers: 5, iterations: 100}, + {interval: 500, workers: 1, iterations: 5, vfs: 'opfs-wl'}, + {interval: 500, workers: 2, iterations: 10, vfs: 'opfs-wl'}, + {interval: 500, workers: 5, iterations: 20, vfs: 'opfs-wl'}, ]){ const li = document.createElement('li'); eTestLinks.appendChild(li); @@ -176,4 +177,4 @@ // Have to delay onmessage assignment until after the loop // to avoid that early workers get an undue head start. workers.forEach((w)=>w.onmessage = workers.onmessage); -})(self); +})(globalThis); diff --git a/ext/wasm/tests/opfs/concurrency/worker.js b/ext/wasm/tests/opfs/concurrency/worker.js index 75c2b8a64d..ccd1df932a 100644 --- a/ext/wasm/tests/opfs/concurrency/worker.js +++ b/ext/wasm/tests/opfs/concurrency/worker.js @@ -68,7 +68,7 @@ globalThis.sqlite3InitModule().then(async function(sqlite3){ filename: 'file:'+dbName+'?opfs-unlock-asap='+options.unlockAsap, flags: 'c' }); - sqlite3.capi.sqlite3_busy_timeout(db.pointer, 15000); + sqlite3.capi.sqlite3_busy_timeout(db.pointer, 5000); } db.transaction((db)=>{ db.exec([ @@ -121,8 +121,11 @@ globalThis.sqlite3InitModule().then(async function(sqlite3){ if(interval.error || maxIterations === interval.count){ finish(); }else{ - const jitter = (Math.random()*150 - Math.random()*150) | 0; - setTimeout(timer, interval.delay + jitter); + const jitter = (Math.random()>=0.5) + ? ((Math.random()*100 - Math.random()*100) | 0) + : 0; + const n = interval.delay + jitter; + setTimeout(timer, n<50 ? interval.delay : n); } }, interval.delay); }/*run()*/; diff --git a/manifest b/manifest index 6c368d0569..ecc54ab16c 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\ssome\sjitter\sand\sduration\sinfo\sto\sthe\sopfs\sconcurrency\stester.\sEnsure\sthat\sAtomics.waitAsync()\sis\savailable\sfor\sopfs-wl.\sFurther\stesting\shas\sshown\sthat\sWeb\sopfs-wl\sis\sconsistently\sfairer\sabout\sdoling\sout\scontested\slocks\sbut\sit's\sdog\sslow\scompared\sto\sthe\soriginal\sVFS\sunder\smoderate\sconcurrency.\sIn\ssingle-connection\suse\sthey're\seffectively\son\spar. -D 2026-03-06T23:50:44.079 +C For\sbackwards\scompatibility,\sensure\sthat\sthe\s"opfs"\sVFS\sdoes\snot\sspecifically\srequire\sAtomics.waitAsync()\s(a\snew\srequirement\sof\s"opfs-wl"),\sbut\smake\suse\sof\sit\sif\savailable.\sOnly\sapply\sjitter\sto\sthe\sconcurrency\stest\sruns\sat\srandom\sintervals. +D 2026-03-07T01:01:13.846 F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea @@ -594,7 +594,7 @@ 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 341468dc0f322d98b032c6922c9865ef9028fafc8f713b950c5ff2c0305e2c87 +F ext/wasm/api/sqlite3-opfs-async-proxy.c-pp.js 296b3d3701c3b42918357e4d9e5d82c5321b371115fb51a8a405c2eaeebd9f0f 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 @@ -645,9 +645,9 @@ F ext/wasm/test-opfs-vfs.js 1618670e466f424aa289859fe0ec8ded223e42e9e69b5c851f80 F ext/wasm/tester1-worker.c-pp.html d0032241d0b24d996cf1c4dd0dde364189693af9b5c986e48af7d3d720fcd244 F ext/wasm/tester1.c-pp.html 52d88fe2c6f21a046030a36410b4839b632f4424028197a45a3d5669ea724ddb 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 b315a5480197e8da80c626d21b7939a485784bc6a13ec681115d4f5384c9b971 -F ext/wasm/tests/opfs/concurrency/worker.js b247316330d2c11817b6debc7d16e4cbc51c689f56a4cb5d6c4ec4a2bca45ca7 +F ext/wasm/tests/opfs/concurrency/index.html c8ac239f6fb45440adbdddb33a0fde2c61a20799189d60b8926be702a27dd226 +F ext/wasm/tests/opfs/concurrency/test.js 520061e6a624fe0a7c07cf3438cb6b9b7c9dcbd606e644ccb5fb52518c48b342 +F ext/wasm/tests/opfs/concurrency/worker.js 90e51ff3e781a6e08f265cbaaa993bb10eb0b798893b78cf4d8a8a8b3b561392 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 12dcd5425967b6306f3e3178babeac130962edb78e6298d545a429a1a20457cc -R 587bc730112047fe7757e9bed1469d73 +P a9aecc987512d60f2663973f43c769cf086fc14149edfbcb18c0aec9f5aa3dbf +R 825063f48a3ce6fa65e33b9852885bae U stephan -Z cdae14ec494ba9a4ab6ec30704fa3278 +Z a8fffcaa59d459a1613276d07e6e1f6d # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 3ea9463f59..a5dd212704 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -a9aecc987512d60f2663973f43c769cf086fc14149edfbcb18c0aec9f5aa3dbf +f2175f526c00cfe562e8f332eb197b5ef2c3d6be1fff2aab1566c2c533a293ac