From: stephan Date: Fri, 6 Mar 2026 16:04:21 +0000 (+0000) Subject: This Web Lock impl can reliably run a single OPFS connection but rather unreliably... X-Git-Tag: major-release~100^2~13 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=7b9cd03c0ad4b7bdfc3f07434345a5ad7496d47a;p=thirdparty%2Fsqlite.git This Web Lock impl can reliably run a single OPFS connection but rather unreliably 'loses' workers with higher counts, presumably due to deadlock or deadly embrace (how _all_ of them can deadlock at once is unclear, but clearly a bug). FossilOrigin-Name: d4e8583e2e80665adfe4e814adb6c219936af1dcac4105795045cb1a7b1e4864 --- diff --git a/ext/wasm/api/opfs-common-shared.c-pp.js b/ext/wasm/api/opfs-common-shared.c-pp.js index 6540939b44..2020184173 100644 --- a/ext/wasm/api/opfs-common-shared.c-pp.js +++ b/ext/wasm/api/opfs-common-shared.c-pp.js @@ -446,7 +446,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ - Call opfvs.bindVfs() */ - opfsUtil.initOptions = function(options, callee){ + opfsUtil.initOptions = function callee(options, callee){ const urlParams = new URL(globalThis.location.href).searchParams; if(urlParams.has('opfs-disable')){ //sqlite3.config.warn('Explicitly not installing "opfs" VFS due to opfs-disable flag.'); @@ -458,19 +458,22 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ return; } options = util.nu(options); - if(undefined===options.verbose){ - options.verbose = urlParams.has('opfs-verbose') - ? (+urlParams.get('opfs-verbose') || 2) : 1; - } - if(undefined===options.sanityChecks){ - options.sanityChecks = urlParams.has('opfs-sanity-check'); - } - if(undefined===options.proxyUri){ - options.proxyUri = callee.defaultProxyUri; - } + options.verbose ??= urlParams.has('opfs-verbose') + ? (+urlParams.get('opfs-verbose') || 2) : 1; + options.sanityChecks ??= urlParams.has('opfs-sanity-check'); + options.proxyUri ??= callee.defaultProxyUri; if('function' === typeof options.proxyUri){ options.proxyUri = options.proxyUri(); } + if( false ){ + /* This ends up with the same values for all Worker instances. */ + callee.counter ??= 0; + ++callee.counter; + options.workerId ??= urlParams.get('opfs-async-proxy-id') ?? callee.counter; + }else{ + options.workerId ??= (Math.random() * 10000000) | 0; + } + //sqlite3.config.warn("opfsUtil options =",JSON.stringify(options), 'urlParams =', urlParams); return options; }; @@ -585,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 ? 100 : 150; + state.asyncIdleWaitTime = isWebLocker ? 150 : 150; /** Whether the async counterpart should log exceptions to @@ -1089,7 +1092,12 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ promiseWasRejected = false; return promiseResolve_(sqlite3); }; - options.proxyUri += '?vfs='+vfsName; + let proxyUri = options.proxyUri +( + (options.proxyUri.indexOf('?')<0) ? '?' : '&' + )+'vfs='+vfsName; + if( options.workerId ){ + proxyUri += '&opfs-async-proxy-id='+encodeURIComponent(options.workerId); + } const W = opfsVfs.worker = //#if target:es6-bundler-friendly (()=>{ @@ -1102,9 +1110,9 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ } })(); //#elif target:es6-module - new Worker(new URL(options.proxyUri, import.meta.url)); + new Worker(new URL(proxyUri, import.meta.url)); //#else - new Worker(options.proxyUri); + new Worker(proxyUri); //#endif let zombieTimer = setTimeout(()=>{ /* At attempt to work around a browser-specific quirk in which 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 0fc23540e1..5c6a11dfd2 100644 --- a/ext/wasm/api/sqlite3-opfs-async-proxy.c-pp.js +++ b/ext/wasm/api/sqlite3-opfs-async-proxy.c-pp.js @@ -54,7 +54,9 @@ const urlParams = new URL(globalThis.location.href).searchParams; if( !urlParams.has('vfs') ){ throw new Error("Expecting vfs=opfs|opfs-wl URL argument for this worker"); } -const isWebLocker = 'opfs-wl'===urlParams.get('vfs'); +const workerId = urlParams.get('opfs-async-proxy-id') + ?? 'unnamed'; +const isWebLocker = true; //'opfs-wl'===urlParams.get('vfs'); const wPost = (type,...args)=>postMessage({type, payload:args}); const installAsyncProxy = function(){ const toss = function(...args){throw new Error(args.join(' '))}; @@ -70,6 +72,10 @@ const installAsyncProxy = function(){ this API. */ const state = Object.create(null); + + /* initS11n() is preprocessor-injected so that we have identical + copies in the synchronous and async halves. This side does not + load the SQLite library, so does not have access to that copy. */ //#define opfs-async-proxy //#include api/opfs-common-inline.c-pp.js //#undef opfs-async-proxy @@ -82,7 +88,7 @@ const installAsyncProxy = function(){ 2 = warnings and errors 3 = debug, warnings, and errors */ - state.verbose = 1; + state.verbose = 2; const loggers = { 0:console.error.bind(console), @@ -90,7 +96,7 @@ const installAsyncProxy = function(){ 2:console.log.bind(console) }; const logImpl = (level,...args)=>{ - if(state.verbose>level) loggers[level]("OPFS asyncer:",...args); + if(state.verbose>level) loggers[level]('opfs-async-proxy',workerId+":",...args); }; const log = (...args)=>logImpl(2, ...args); const warn = (...args)=>logImpl(1, ...args); @@ -297,13 +303,26 @@ const installAsyncProxy = function(){ there's another race condition there). That's easy to say but creating a viable test for that condition has proven challenging so far. + + 2026-03-06: + + - baseWaitTime is the number of milliseconds to wait for the + first retry, doubling for each retry. It defaults to + (state.asyncIdleWaitTime*2). + + - maxTries is the number of attempt to make, each one spaced out + by one additional factor of the baseWaitTime (e.g. 300, then 600, + then 900, the 1200...). This MUST be an integer >0 and defaults + to 6. + + Only the Web Locks impl should use the 3rd and 4th parameters. */ - const getSyncHandle = async (fh,opName)=>{ + const getSyncHandle = async (fh,opName, baseWaitTime, maxTries)=>{ if(!fh.syncHandle){ const t = performance.now(); log("Acquiring sync handle for",fh.filenameAbs); - const maxTries = 6, - msBase = state.asyncIdleWaitTime * 2; + const msBase = baseWaitTime ?? (state.asyncIdleWaitTime * 2); + maxTries ??= 6; let i = 1, ms = msBase; for(; true; ms = msBase * ++i){ try { @@ -337,6 +356,9 @@ const installAsyncProxy = function(){ /** Stores the given value at state.sabOPView[state.opIds.rc] and then Atomics.notify()'s it. + + The opName is only used for logging and debugging - all result + codes are expected on the same state.sabOPView slot. */ const storeAndNotify = (opName, value)=>{ log(opName+"() => notify(",value,")"); @@ -466,24 +488,12 @@ const installAsyncProxy = function(){ await releaseImplicitLock(fh); storeAndNotify('xFileSize', rc); }, - xLock: async function(fid/*sqlite3_file pointer*/, - lockType/*SQLITE_LOCK_...*/){ - const fh = __openFiles[fid]; - let rc = 0; - const oldLockType = fh.xLock; - fh.xLock = lockType; - if( !fh.syncHandle ){ - try { - await getSyncHandle(fh,'xLock'); - __implicitLocks.delete(fid); - }catch(e){ - state.s11n.storeException(1,e); - rc = GetSyncHandleError.convertRc(e,state.sq3Codes.SQLITE_IOERR_LOCK); - fh.xLock = oldLockType; - } - } - storeAndNotify('xLock',rc); - }, + /** + The first argument is semantically invalid here - it's an + address in the synchronous side's heap. We can do nothing with + it here except use it as a unique-per-file identifier. + i.e. a lookup key. + */ xOpen: async function(fid/*sqlite3_file pointer*/, filename, flags/*SQLITE_OPEN_...*/, opfsFlags/*OPFS_...*/){ @@ -575,22 +585,6 @@ const installAsyncProxy = function(){ await releaseImplicitLock(fh); storeAndNotify('xTruncate',rc); }, - xUnlock: async function(fid/*sqlite3_file pointer*/, - lockType/*SQLITE_LOCK_...*/){ - let rc = 0; - const fh = __openFiles[fid]; - if( fh.syncHandle - && state.sq3Codes.SQLITE_LOCK_NONE===lockType - /* Note that we do not differentiate between lock types in - this VFS. We're either locked or unlocked. */ ){ - try { await closeSyncHandle(fh) } - catch(e){ - state.s11n.storeException(1,e); - rc = state.sq3Codes.SQLITE_IOERR_UNLOCK; - } - } - storeAndNotify('xUnlock',rc); - }, xWrite: async function(fid/*sqlite3_file pointer*/,n,offset64){ let rc; const fh = __openFiles[fid]; @@ -611,6 +605,187 @@ const installAsyncProxy = function(){ } }/*vfsAsyncImpls*/; + if( isWebLocker ){ + /* We require separate xLock() and xUnlock() implementations for the + original and Web Lock implementations. The ones in this block + are for the WebLock impl. */ + + /** Registry of active Web Locks: fid -> { mode, resolveRelease } */ + const __activeWebLocks = Object.create(null); + + vfsAsyncImpls.xLock = async function(fid/*sqlite3_file pointer*/, + lockType/*SQLITE_LOCK_...*/, + 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'; + const existing = __activeWebLocks[fid]; + if( existing ){ + if( existing.mode === requestedMode + || (existing.mode === 'exclusive' + && requestedMode === 'shared') ) { + storeAndNotify(whichOp, 0); + existing.mode = requestedMode/* ??? */; + fh.lockType = lockType; + 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. + */ + if( 0 ){ + /* 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. */ + await closeSyncHandle(fh); + } + existing.resolveRelease(); + delete __activeWebLocks[fid]; + } + + const oldLockType = fh.xLock; + let didNotify = false; + 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; + __implicitLocks.delete(fid); + if( 1 ){ + /* Make ONE attempt to get the handle, but with a + higher-than-default retry-wait time. */ + await getSyncHandle(fh, 'xLock', 1000, 5); + }else{ + /* Try to get a lock until either we get one or trying to + results in a "not found" error (see getSyncHandle() docs). */ + while( !fh.syncHandle ){ + try{ + await getSyncHandle(fh, 'xLock', 1000, 3); + }catch(e){ + const rc = GetSyncHandleError.convertRc(e, 0); + if( rc === state.sq3Codes.SQLITE_CANTOPEN ){ + /* File was deleted - see getSyncHandle() */ + throw e; + } + error("xLock() still waiting to unlock SyncAccessHandle",fh); + } + } + } + error("xLock() SAH acquired.", fh); + const releasePromise = new Promise((resolveRelease) => { + __activeWebLocks[fid] = { mode: requestedMode, resolveRelease }; + }); + didNotify = true; + storeAndNotify(whichOp, 0) /* Unblock the C side */; + resolveWaitLoop(0) /* Unblock waitLoop() */; + await releasePromise; // Hold the lock until xUnlock + }).catch(e=>{ + /** + We have(?) a potential deadlock situation: if the above + throws, we can't just blindly storeAndNotify() here to + unlock the C side, as it might interfere with an + unrelated operation. The `didNotify` check here assumes + that any exception which can be thrown will happen before + the above `didNotify=true`. e.g. getSyncHandle() can + throw. Apropos: we probably need to be able to configure + the async side with busy timeout values, and try until + that limit is reached, or tell it to wait indefinitely. + + Because waitLoop() is `await`ing on this Promise, we can + be sure that the following storeAndNotify() is not + crossing wires with a different operation. + */ + fh.xLock = oldLockType; + error("Exception acquiring Web Lock", e); + if( !didNotify ){ + state.s11n.storeException(1, e); + const rc = GetSyncHandleError.convertRc(e, state.sq3Codes.SQLITE_IOERR_LOCK); + storeAndNotify(whichOp, rc); + } + throw e /* what else can we do? */; + }) + }); + }; + + vfsAsyncImpls.xUnlock = async function(fid/*sqlite3_file pointer*/, + lockType/*SQLITE_LOCK_...*/){ + const fh = __openFiles[fid]; + const existing = __activeWebLocks[fid]; + if( !existing ){ + await closeSyncHandle(fh); + storeAndNotify('xUnlock', 0); + return 0; + } + error("xUnlock()",fid, lockType, fh); + let rc = 0; + if( lockType === state.sq3Codes.SQLITE_LOCK_NONE ){ + /* SQLite usually unlocks all the way to NONE */ + existing.resolveRelease(); + delete __activeWebLocks[fid]; + try {await closeSyncHandle(fh)} + catch(e){ + state.s11n.storeException(1,e); + rc = state.sq3Codes.SQLITE_IOERR_UNLOCK; + } + }else if( lockType === state.sq3Codes.SQLITE_LOCK_SHARED + && existing.mode === 'exclusive' ){ + /* downgrade Exclusive -> Shared */ + existing.resolveRelease(); + delete __activeWebLocks[fid]; + return vfsAsyncImpls.xLock(fid, lockType, true); + }else{ + /* ??? */ + error("xUnlock() unhandled condition", fh); + } + storeAndNotify('xUnlock', rc); + if( 0===rc ) fh.lockType = lockType; + return 0; + } + + }else{ + + /* Original/"legacy" xLock() and xUnlock() */ + vfsAsyncImpls.xLock = async function(fid/*sqlite3_file pointer*/, + lockType/*SQLITE_LOCK_...*/){ + const fh = __openFiles[fid]; + let rc = 0; + const oldLockType = fh.xLock; + fh.xLock = lockType; + if( !fh.syncHandle ){ + try { + await getSyncHandle(fh,'xLock'); + __implicitLocks.delete(fid); + }catch(e){ + state.s11n.storeException(1,e); + rc = GetSyncHandleError.convertRc(e,state.sq3Codes.SQLITE_IOERR_LOCK); + fh.xLock = oldLockType; + } + } + storeAndNotify('xLock',rc); + }; + + vfsAsyncImpls.xUnlock = async function(fid/*sqlite3_file pointer*/, + lockType/*SQLITE_LOCK_...*/){ + let rc = 0; + const fh = __openFiles[fid]; + if( fh.syncHandle + && state.sq3Codes.SQLITE_LOCK_NONE===lockType + /* Note that we do not differentiate between lock types in + this VFS. We're either locked or unlocked. */ ){ + try { await closeSyncHandle(fh) } + catch(e){ + state.s11n.storeException(1,e); + rc = state.sq3Codes.SQLITE_IOERR_UNLOCK; + } + } + storeAndNotify('xUnlock',rc); + } + + }/*xLock() and xUnlock() impls*/ + const waitLoop = async function f(){ if( !f.inited ){ f.inited = true; @@ -645,7 +820,7 @@ const installAsyncProxy = function(){ an exception string written by the upcoming operation */ ) || []; - //warn("waitLoop() whichOp =",opId, hnd, args); + //error("waitLoop() whichOp =",opId, f.opHandlers[opId].key, args); await hnd(...args); }catch(e){ error('in waitLoop():', e); @@ -656,7 +831,7 @@ const installAsyncProxy = function(){ navigator.storage.getDirectory().then(function(d){ state.rootDir = d; globalThis.onmessage = function({data}){ - warn(globalThis.location.href,"onmessage()",data); + //log(globalThis.location.href,"onmessage()",data); switch(data.type){ case 'opfs-async-init':{ /* Receive shared state from synchronous partner */ diff --git a/ext/wasm/api/sqlite3-vfs-opfs.c-pp.js b/ext/wasm/api/sqlite3-vfs-opfs.c-pp.js index 89b52c836d..6d0988d6d7 100644 --- a/ext/wasm/api/sqlite3-vfs-opfs.c-pp.js +++ b/ext/wasm/api/sqlite3-vfs-opfs.c-pp.js @@ -86,43 +86,31 @@ const installOpfsVfs = async function callee(options){ const capi = sqlite3.capi, state = opfsUtil.createVfsState('opfs', options), opfsVfs = state.vfs, - metrics = opfsVfs.metrics.counters, mTimeStart = opfsVfs.mTimeStart, mTimeEnd = opfsVfs.mTimeEnd, opRun = opfsVfs.opRun, + debug = (...args)=>sqlite3.config.debug("opfs:",...args), __openFiles = opfsVfs.__openFiles; - /* At this point, createVfsState() has populated state and - opfsVfs with any code common to both the "opfs" and "opfs-wl" + //debug("options:",JSON.stringify(options)); + /* At this point, createVfsState() has populated `state` and + `opfsVfs` with any code common to both the "opfs" and "opfs-wl" VFSes. Now comes the VFS-dependent work... */ return opfsVfs.bindVfs(util.nu({ xLock: function(pFile,lockType){ mTimeStart('xLock'); - ++metrics.xLock.count; + debug("xLock()..."); const f = __openFiles[pFile]; - let rc = 0; - /* All OPFS locks are exclusive locks. If xLock() has - previously succeeded, do nothing except record the lock - type. If no lock is active, have the async counterpart - lock the file. */ - if( f.lockType ) { - f.lockType = lockType; - }else{ - rc = opRun('xLock', pFile, lockType); - if( 0===rc ) f.lockType = lockType; - } + const rc = opRun('xLock', pFile, lockType); + debug("xLock() rc ",rc); + if( 0===rc ) f.lockType = lockType; mTimeEnd(); return rc; }, xUnlock: function(pFile,lockType){ mTimeStart('xUnlock'); - ++metrics.xUnlock.count; const f = __openFiles[pFile]; - let rc = 0; - if( capi.SQLITE_LOCK_NONE === lockType - && f.lockType ){ - rc = opRun('xUnlock', pFile, lockType); - } + const rc = opRun('xUnlock', pFile, lockType); if( 0===rc ) f.lockType = lockType; mTimeEnd(); return rc; @@ -137,15 +125,17 @@ const installOpfsVfs = async function callee(options){ OpfsDb.prototype = Object.create(sqlite3.oo1.DB.prototype); sqlite3.oo1.OpfsDb = OpfsDb; OpfsDb.importDb = opfsUtil.importDb; - sqlite3.oo1.DB.dbCtorHelper.setVfsPostOpenCallback( - opfsVfs.pointer, - function(oo1Db, sqlite3){ - /* Set a relatively high default busy-timeout handler to - help OPFS dbs deal with multi-tab/multi-worker - contention. */ - sqlite3.capi.sqlite3_busy_timeout(oo1Db, 10000); - } - ); + if( false ){ + sqlite3.oo1.DB.dbCtorHelper.setVfsPostOpenCallback( + opfsVfs.pointer, + function(oo1Db, sqlite3){ + /* Set a relatively high default busy-timeout handler to + help OPFS dbs deal with multi-tab/multi-worker + contention. */ + sqlite3.capi.sqlite3_busy_timeout(oo1Db, 10000); + } + ); + } }/*extend sqlite3.oo1*/ })/*bindVfs()*/; }/*installOpfsVfs()*/; diff --git a/ext/wasm/tester1.c-pp.js b/ext/wasm/tester1.c-pp.js index 083b5eca44..8c0ba19165 100644 --- a/ext/wasm/tester1.c-pp.js +++ b/ext/wasm/tester1.c-pp.js @@ -129,17 +129,6 @@ globalThis.sqlite3InitModule = sqlite3InitModule; }; } } - const reportFinalTestStatus = function(pass){ - if(isUIThread()){ - let e = document.querySelector('#color-target'); - e.classList.add(pass ? 'tests-pass' : 'tests-fail'); - e = document.querySelector('title'); - e.innerText = (pass ? 'PASS' : 'FAIL') + ': ' + e.innerText; - }else{ - postMessage({type:'test-result', payload:{pass}}); - } - TestUtil.checkHeapSize(true); - }; const log = (...args)=>{ //console.log(...args); logClass('',...args); @@ -157,6 +146,19 @@ globalThis.sqlite3InitModule = sqlite3InitModule; console.debug('tester1',...args); }; + const reportFinalTestStatus = function(pass){ + debug("Final test status:",pass); + if(isUIThread()){ + let e = document.querySelector('#color-target'); + e.classList.add(pass ? 'tests-pass' : 'tests-fail'); + e = document.querySelector('title'); + e.innerText = (pass ? 'PASS' : 'FAIL') + ': ' + e.innerText; + }else{ + postMessage({type:'test-result', payload:{pass}}); + } + TestUtil.checkHeapSize(true); + }; + const toss = (...args)=>{ error(...args); throw new Error(args.join(' ')); diff --git a/ext/wasm/tests/opfs/concurrency/worker.js b/ext/wasm/tests/opfs/concurrency/worker.js index 77e320e9af..365900c2d5 100644 --- a/ext/wasm/tests/opfs/concurrency/worker.js +++ b/ext/wasm/tests/opfs/concurrency/worker.js @@ -1,15 +1,17 @@ -importScripts( - (new URL(globalThis.location.href).searchParams).get('sqlite3.dir') + '/sqlite3.js' -); -//const sqlite3InitModule = (await import("../../../jswasm/sqlite3.mjs", )).default; +'use strict'; +const urlArgs = new URL(globalThis.location.href).searchParams; +const options = { + workerName: urlArgs.get('workerId') || Math.round(Math.random()*10000), + unlockAsap: urlArgs.get('opfs-unlock-asap') || 0, + vfs: urlArgs.get('vfs') +}; +const jsSqlite = urlArgs.get('sqlite3.dir') + +'/sqlite3.js?opfs-async-proxy-id=' + +options.workerName; +importScripts(jsSqlite)/*Sigh - URL args are not propagated this way*/; +//const sqlite3InitModule = (await import(jsSqlite)).default; globalThis.sqlite3InitModule.__isUnderTest = true; globalThis.sqlite3InitModule().then(async function(sqlite3){ - const urlArgs = new URL(globalThis.location.href).searchParams; - const options = { - workerName: urlArgs.get('workerId') || Math.round(Math.random()*10000), - unlockAsap: urlArgs.get('opfs-unlock-asap') || 0 /*EXPERIMENTAL*/, - vfs: urlArgs.get('vfs') - }; const wPost = (type,...payload)=>{ postMessage({type, worker: options.workerName, payload}); }; @@ -41,7 +43,9 @@ globalThis.sqlite3InitModule().then(async function(sqlite3){ db.close(); } if(interval.error){ - wPost('failed',"Ending work after interval #"+interval.count, + stderr("Ending work at interval #"+interval.count, + "due to error:", interval.error); + wPost('failed', "at interval #"+interval.count, "due to error:",interval.error); }else{ wPost('finished',"Ending work after",interval.count,"intervals."); diff --git a/manifest b/manifest index 94e2e47dac..78515cb419 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Remove\san\sextraneous\sOPFS\smetrics\sincrement. -D 2026-03-06T11:49:36.430 +C This\sWeb\sLock\simpl\scan\sreliably\srun\sa\ssingle\sOPFS\sconnection\sbut\srather\sunreliably\s'loses'\sworkers\swith\shigher\scounts,\spresumably\sdue\sto\sdeadlock\sor\sdeadly\sembrace\s(how\s_all_\sof\sthem\scan\sdeadlock\sat\sonce\sis\sunclear,\sbut\sclearly\sa\sbug). +D 2026-03-06T16:04:21.050 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 ecc3c69dc8a3b676f6999ef1b2ac4be825b7952ce5acbe86941f4f2f38607cb7 +F ext/wasm/api/opfs-common-shared.c-pp.js 7bfbf3a5ce1b558ec3d0b3e14f375e9f6003b6bb49c58480e2fbf2726cf59b2c 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 5f95c210183d203c7bc5be4a8139cc7fa2e50810c306bb2651648c32ab419fcf +F ext/wasm/api/sqlite3-opfs-async-proxy.c-pp.js f471178310dd8c27f46aaa3bb2bdfb8e0b695b37a54b9a9c66b820e413b55f68 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 f3b7296480984bcc6050fe9724a8b215c405977dd69daea7145ece25751e4b33 +F ext/wasm/api/sqlite3-vfs-opfs.c-pp.js 3fde62ac67c963ee04030cf279357bb19b98a420973f55245c44396828b582d6 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 @@ -644,10 +644,10 @@ F ext/wasm/test-opfs-vfs.html 1f2d672f3f3fce810dfd48a8d56914aba22e45c6834e262555 F ext/wasm/test-opfs-vfs.js 1618670e466f424aa289859fe0ec8ded223e42e9e69b5c851f809baaaca1a00c F ext/wasm/tester1-worker.c-pp.html d0032241d0b24d996cf1c4dd0dde364189693af9b5c986e48af7d3d720fcd244 F ext/wasm/tester1.c-pp.html 52d88fe2c6f21a046030a36410b4839b632f4424028197a45a3d5669ea724ddb -F ext/wasm/tester1.c-pp.js 6b946cd6d4da130dbae4a401057716d27117ca02cad2ea8c29ae9c46c675d618 +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 3425e6dad755a1c69a6efc63a47a3ade4e7f0a9a138994ba37f996571fb46288 +F ext/wasm/tests/opfs/concurrency/worker.js d0303b1403867e97455f7563285af3eb4471961b19bc22e45d021d896d48e27c 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 521bb140b7ed237c118ac9094732d06907229a6ff385502e850c679bd623fd58 -R 334abfd624b27a29d02bb7116f94ced4 +P bf3548a37712e848c7a9cadfdc1669a2be572ea0a0c28d84c157ab30f8c30c44 +R aeb14d0bd734de11301263e4e120c1e3 U stephan -Z 944b558939ff5f7ba7e037ff2a332b5b +Z c3b8772f8a9f1db66a44dbdb2829ac29 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 48801140c5..17f5b7a2ef 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -bf3548a37712e848c7a9cadfdc1669a2be572ea0a0c28d84c157ab30f8c30c44 +d4e8583e2e80665adfe4e814adb6c219936af1dcac4105795045cb1a7b1e4864