1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-07-27 20:41:58 +03:00

Add the pause/unpause capability to the opfs-sahpool VFS, as discussed in [forum:fe8cdb8431c|forum thread fe8cdb8431c]. Summary: this gives clients a way to eke some degree of multi-page/tab/Worker concurrency out of this VFS but requires that coordination to be implemented client-side, e.g. via a SharedWorker or WebLocks.

FossilOrigin-Name: b5dbd521951e129b4dec69f191a872500dbf387b34a8479ad58b053ffcccbab9
This commit is contained in:
stephan
2025-02-20 04:14:26 +00:00
10 changed files with 483 additions and 38 deletions

View File

@ -103,7 +103,7 @@ else
ifeq (,$(filter $(OPTIMIZED_TARGETS),$(MAKECMDGOALS)))
$(info ==============================================================)
$(info == Development build. Make one of (dist, snapshot) for a)
$(info == smaller release build.)
$(info == smaller and faster release build.)
$(info ==============================================================)
endif
endif

View File

@ -501,22 +501,10 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
currently-opened client-specified filenames. */
getFileNames(){
const rc = [];
const iter = this.#mapFilenameToSAH.keys();
for(const n of iter) rc.push(n);
for(const n of this.#mapFilenameToSAH.keys()) rc.push(n);
return rc;
}
// #createFileObject(sah,clientName,opaqueName){
// const f = Object.assign(Object.create(null),{
// clientName, opaqueName
// });
// this.#mapSAHToMeta.set(sah, f);
// return f;
// }
// #unmapFileObject(sah){
// this.#mapSAHToMeta.delete(sah);
// }
/**
Adds n files to the pool's capacity. This change is
persistent across settings. Returns a Promise which resolves
@ -557,8 +545,9 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
}
/**
Releases all currently-opened SAHs. The only legal
operation after this is acquireAccessHandles().
Releases all currently-opened SAHs. The only legal operation
after this is acquireAccessHandles() or (if this is called from
pauseVfs()) either of isPaused() or unpauseVfs().
*/
releaseAccessHandles(){
for(const ah of this.#mapSAHToName.keys()) ah.close();
@ -568,17 +557,21 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
}
/**
Opens all files under this.vfsDir/this.#dhOpaque and acquires
a SAH for each. returns a Promise which resolves to no value
but completes once all SAHs are acquired. If acquiring an SAH
throws, SAHPool.$error will contain the corresponding
exception.
Opens all files under this.vfsDir/this.#dhOpaque and acquires a
SAH for each. Returns a Promise which resolves to no value but
completes once all SAHs are acquired. If acquiring an SAH
throws, this.$error will contain the corresponding Error
object.
If it throws, it releases any SAHs which it may have
acquired before the exception was thrown, leaving the VFS in a
well-defined but unusable state.
If clearFiles is true, the client-stored state of each file is
cleared when its handle is acquired, including its name, flags,
and any data stored after the metadata block.
*/
async acquireAccessHandles(clearFiles){
async acquireAccessHandles(clearFiles=false){
const files = [];
for await (const [name,h] of this.#dhOpaque){
if('file'===h.kind){
@ -832,12 +825,18 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
Removes this object's sqlite3_vfs registration and shuts down
this object, releasing all handles, mappings, and whatnot,
including deleting its data directory. There is currently no
way to "revive" the object and reaquire its resources.
way to "revive" the object and reaquire its
resources. Similarly, there is no recovery strategy if removal
of any given SAH fails, so such errors are ignored by this
function.
This function is intended primarily for testing.
Resolves to true if it did its job, false if the
VFS has already been shut down.
@see pauseVfs()
@see unpauseVfs()
*/
async removeVfs(){
if(!this.#cVfs.pointer || !this.#dhOpaque) return false;
@ -853,13 +852,77 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
);
this.#dhVfsRoot = this.#dhVfsParent = undefined;
}catch(e){
sqlite3.config.error(this.vfsName,"removeVfs() failed:",e);
sqlite3.config.error(this.vfsName,"removeVfs() failed with no recovery strategy:",e);
/*otherwise ignored - there is no recovery strategy*/
}
return true;
}
/**
"Pauses" this VFS by unregistering it from SQLite and
relinquishing all open SAHs, leaving the associated files
intact. If this object is already paused, this is a
no-op. Returns this object.
This function throws if SQLite has any opened file handles
hosted by this VFS, as the alternative would be to invoke
Undefined Behavior by closing file handles out from under the
library. Similarly, automatically closing any database handles
opened by this VFS would invoke Undefined Behavior in
downstream code which is holding those pointers.
If this function throws due to open file handles then it has
no side effects. If the OPFS API throws while closing handles
then the VFS is left in an undefined state.
@see isPaused()
@see unpauseVfs()
*/
pauseVfs(){
if(this.#mapS3FileToOFile_.size>0){
sqlite3.SQLite3Error.toss(
capi.SQLITE_MISUSE, "Cannot pause VFS",
this.vfsName,"because it has opened files."
);
}
if(this.#mapSAHToName.size>0){
capi.sqlite3_vfs_unregister(this.vfsName);
this.releaseAccessHandles();
}
return this;
}
/**
Returns true if this pool is currently paused else false.
@see pauseVfs()
@see unpauseVfs()
*/
isPaused(){
return 0===this.#mapSAHToName.size;
}
/**
"Unpauses" this VFS, reacquiring all SAH's and (if successful)
re-registering it with SQLite. This is a no-op if the VFS is
not currently paused.
The returned Promise resolves to this object. See
acquireAccessHandles() for how it behaves if it throws due to
SAH acquisition failure.
@see isPaused()
@see pauseVfs()
*/
async unpauseVfs(){
if(0===this.#mapSAHToName.size){
return this.acquireAccessHandles(false).
then(()=>capi.sqlite3_vfs_register(this.#cVfs, 0),this);
}
return this;
}
//! Documented elsewhere in this file.
exportFile(name){
const sah = this.#mapFilenameToSAH.get(name) || toss("File not found:",name);
@ -984,6 +1047,10 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
async removeVfs(){ return this.#p.removeVfs() }
pauseVfs(){ this.#p.pauseVfs(); return this; }
async unpauseVfs(){ return this.#p.unpauseVfs().then(()=>this); }
isPaused(){ return this.#p.isPaused() }
}/* class OpfsSAHPoolUtil */;
/**
@ -1217,6 +1284,41 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
Clears all client-defined state of all SAHs and makes all of them
available for re-use by the pool. Results are undefined if any such
handles are currently in use, e.g. by an sqlite3 db.
APIs specific to the "pause" capability (added in version 3.49):
Summary: "pausing" the VFS disassociates it from SQLite and
relinquishes its SAHs so that they may be opened by another
instance of this VFS (running in a separate tab/page or Worker).
"Unpausing" it takes back control, if able.
- pauseVfs()
"Pauses" this VFS by unregistering it from SQLite and
relinquishing all open SAHs, leaving the associated files intact.
This enables pages/tabs to coordinate semi-concurrent usage of
this VFS. If this object is already paused, this is a
no-op. Returns this object. Throws if SQLite has any opened file
handles hosted by this VFS. If this function throws due to open
file handles then it has no side effects. If the OPFS API throws
while closing handles then the VFS is left in an undefined state.
- isPaused()
Returns true if this VFS is paused, else false.
- [async] unpauseVfs()
Restores the VFS to an active state after having called
pauseVfs() on it. This is a no-op if the VFS is not paused. The
returned Promise resolves to this object on success. A rejected
Promise means there was a problem reacquiring the SAH handles
(possibly because they're in use by another instance or have
since been removed). Generically speaking, there is no recovery
strategy for that type of error, but if the problem is simply
that the OPFS files are locked, then a later attempt to unpause
it, made after the concurrent instance releases the SAHs, may
recover from the situation.
*/
sqlite3.installOpfsSAHPoolVfs = async function(options=Object.create(null)){
options = Object.assign(Object.create(null), optionDefaults, (options||{}));

View File

@ -335,8 +335,8 @@ sqlite3Worker1Promiser.v2 = function(config){
/**
When built as a module, we export sqlite3Worker1Promiser.v2()
instead of sqlite3Worker1Promise() because (A) its interface is more
conventional for ESM usage and (B) the ESM option export option for
this API did not exist until v2 was created, so there's no backwards
conventional for ESM usage and (B) the ESM export option for this
API did not exist until v2 was created, so there's no backwards
incompatibility.
*/
export default sqlite3Worker1Promiser.v2;

View File

@ -119,6 +119,10 @@
<li><a href='tests/opfs/concurrency/index.html'>OPFS concurrency</a>
tests using multiple workers.
</li>
<li><a href='tests/opfs/sahpool/index.html'>OPFS SAHPool cooperative semi-concurrency</a>
demonstrates usage of the OPFS SAHPool VFS's "pause" feature to coordinate
access to a database.
</li>
</ul>
</li>
<li><strong>WASMFS</strong>-specific tests which require that

View File

@ -3189,8 +3189,25 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
db.close();
T.assert(1 === u1.getFileCount());
db = new u2.OpfsSAHPoolDb(dbName);
T.assert(1 === u1.getFileCount());
T.assert(1 === u1.getFileCount())
.mustThrowMatching(
()=>u1.pauseVfs(),
(err)=>{
return capi.SQLITE_MISUSE===err.resultCode
&& /^SQLITE_MISUSE: Cannot pause VFS /.test(err.message);
},
"Cannot pause VFS with opened db."
);
db.close();
T.assert( u2===u2.pauseVfs() )
.assert( u2.isPaused() )
.assert( 0===capi.sqlite3_vfs_find(u2.vfsName) )
.mustThrowMatching(()=>new u2.OpfsSAHPoolDb(dbName),
/.+no such vfs: .+/,
"VFS is not available")
.assert( u2===await u2.unpauseVfs() )
.assert( u2===await u1.unpauseVfs(), "unpause is a no-op if the VFS is not paused" )
.assert( 0!==capi.sqlite3_vfs_find(u2.vfsName) );
const fileNames = u1.getFileNames();
T.assert(1 === fileNames.length)
.assert(dbName === fileNames[0])

View File

@ -0,0 +1,31 @@
<!doctype html>
<html lang="en-us">
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon">
<link rel="stylesheet" href="../../../common/emscripten.css"/>
<link rel="stylesheet" href="../../../common/testing.css"/>
<title>sqlite3 tester: OpfsSAHPool Pausing</title>
<style></style>
</head>
<body><h1 id='color-target'></h1>
<p>
This page provides a <em>very basic</em> demonstration of
"pausing" and "unpausing" the OPFS SAHPool VFS such that
multiple pages or workers can use it by coordinating which
handler may have it open at any given time.
</p>
<div class='input-wrapper'>
<input type='checkbox' id='cb-log-reverse'>
<label for='cb-log-reverse'>Reverse log order?</label>
</div>
<div id='test-output'></div>
<script>(function(){
document.querySelector('h1').innerHTML =
document.querySelector('title').innerHTML;
})();</script>
<script src="sahpool-pausing.js"></script>
</body>
</html>

View File

@ -0,0 +1,183 @@
/*
2025-01-31
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.
***********************************************************************
These tests are specific to the opfs-sahpool VFS and are limited to
demonstrating its pause/unpause capabilities.
Most of this file is infrastructure for displaying results to the
user. Search for runTests() to find where the work actually starts.
*/
'use strict';
(function(){
let logClass;
const mapToString = (v)=>{
switch(typeof v){
case 'number': case 'string': case 'boolean':
case 'undefined': case 'bigint':
return ''+v;
default: break;
}
if(null===v) return 'null';
if(v instanceof Error){
v = {
message: v.message,
stack: v.stack,
errorClass: v.name
};
}
return JSON.stringify(v,undefined,2);
};
const normalizeArgs = (args)=>args.map(mapToString);
const logTarget = document.querySelector('#test-output');
logClass = function(cssClass,...args){
const ln = document.createElement('div');
if(cssClass){
for(const c of (Array.isArray(cssClass) ? cssClass : [cssClass])){
ln.classList.add(c);
}
}
ln.append(document.createTextNode(normalizeArgs(args).join(' ')));
logTarget.append(ln);
};
const cbReverse = document.querySelector('#cb-log-reverse');
//cbReverse.setAttribute('checked','checked');
const cbReverseKey = 'tester1:cb-log-reverse';
const cbReverseIt = ()=>{
logTarget.classList[cbReverse.checked ? 'add' : 'remove']('reverse');
//localStorage.setItem(cbReverseKey, cbReverse.checked ? 1 : 0);
};
cbReverse.addEventListener('change', cbReverseIt, true);
/*if(localStorage.getItem(cbReverseKey)){
cbReverse.checked = !!(+localStorage.getItem(cbReverseKey));
}*/
cbReverseIt();
const log = (...args)=>{
//console.log(...args);
logClass('',...args);
}
const warn = (...args)=>{
console.warn(...args);
logClass('warning',...args);
}
const error = (...args)=>{
console.error(...args);
logClass('error',...args);
};
const toss = (...args)=>{
error(...args);
throw new Error(args.join(' '));
};
const endOfWork = (passed=true)=>{
const eH = document.querySelector('#color-target');
const eT = document.querySelector('title');
if(passed){
log("End of work chain. If you made it this far, you win.");
eH.innerText = 'PASS: '+eH.innerText;
eH.classList.add('tests-pass');
eT.innerText = 'PASS: '+eT.innerText;
}else{
eH.innerText = 'FAIL: '+eH.innerText;
eH.classList.add('tests-fail');
eT.innerText = 'FAIL: '+eT.innerText;
}
};
const nextHandlerQueue = [];
const nextHandler = function(workerId,...msg){
log(workerId,...msg);
(nextHandlerQueue.shift())();
};
const postThen = function(W, msgType, callback){
nextHandlerQueue.push(callback);
W.postMessage({type:msgType});
};
/**
Run a series of operations on an sahpool db spanning two workers.
This would arguably be more legible with Promises, but creating a
Promise-based communication channel for this purpose is left as
an exercise for the reader. An example of such a proxy can be
found in the SQLite source tree:
https://sqlite.org/src/file/ext/wasm/api/sqlite3-worker1-promiser.c-pp.js
*/
const runPyramidOfDoom = function(W1, W2){
postThen(W1, 'vfs-acquire', function(){
postThen(W1, 'db-init', function(){
postThen(W1, 'db-query', function(){
postThen(W1, 'vfs-pause', function(){
postThen(W2, 'vfs-acquire', function(){
postThen(W2, 'db-query', function(){
postThen(W2, 'vfs-remove', endOfWork);
});
});
});
});
});
});
};
const runTests = function(){
log("Running opfs-sahpool pausing tests...");
const wjs = 'sahpool-worker.js?sqlite3.dir=../../../jswasm';
const W1 = new Worker(wjs+'&workerId=w1'),
W2 = new Worker(wjs+'&workerId=w2');
W1.workerId = 'w1';
W2.workerId = 'w2';
let initCount = 0;
const onmessage = function({data}){
//log("onmessage:",data);
switch(data.type){
case 'vfs-acquired':
nextHandler(data.workerId, "VFS acquired");
break;
case 'vfs-paused':
nextHandler(data.workerId, "VFS paused");
break;
case 'vfs-unpaused':
nextHandler(data.workerId, 'VFS unpaused');
break;
case 'vfs-removed':
nextHandler(data.workerId, 'VFS removed');
break;
case 'db-inited':
nextHandler(data.workerId, 'db initialized');
break;
case 'query-result':
nextHandler(data.workerId, 'query result', data.payload);
break;
case 'log':
log(data.workerId, ':', ...data.payload);
break;
case 'error':
error(data.workerId, ':', ...data.payload);
endOfWork(false);
break;
case 'initialized':
log(data.workerId, ': Worker initialized',...data.payload);
if( 2===++initCount ){
runPyramidOfDoom(W1, W2);
}
break;
}
};
W1.onmessage = W2.onmessage = onmessage;
};
runTests();
})();

View File

@ -0,0 +1,104 @@
/*
2025-01-31
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.
***********************************************************************
This file is part of sahpool-pausing.js's demonstration of the
pause/unpause feature of the opfs-sahpool VFS.
*/
const searchParams = new URL(self.location.href).searchParams;
const workerId = searchParams.get('workerId');
const wPost = (type,...args)=>postMessage({type, workerId, payload:args});
const log = (...args)=>wPost('log',...args);
let capi, wasm, S, poolUtil;
const sahPoolConfig = {
name: 'opfs-sahpool-pausable',
clearOnInit: false,
initialCapacity: 3
};
importScripts(searchParams.get('sqlite3.dir') + '/sqlite3.js');
const sqlExec = function(sql){
const db = new poolUtil.OpfsSAHPoolDb('/my.db');
try{
return db.exec(sql);
}finally{
db.close();
}
};
const clog = console.log.bind(console);
globalThis.onmessage = function({data}){
clog(workerId+": onmessage:",data);
switch(data.type){
case 'vfs-acquire':
if( poolUtil ){
poolUtil.unpauseVfs().then(()=>wPost('vfs-unpaused'));
}else{
S.installOpfsSAHPoolVfs(sahPoolConfig).then(pu=>{
poolUtil = pu;
wPost('vfs-acquired');
});
}
break;
case 'db-init':
try{
sqlExec([
"DROP TABLE IF EXISTS mytable;",
"CREATE TABLE mytable(a);",
"INSERT INTO mytable(a) VALUES(11),(22),(33)"
]);
wPost('db-inited');
}catch(e){
wPost('error',e.message);
}
break;
case 'db-query': {
const rc = sqlExec({
sql: 'select * from mytable order by a',
rowMode: 'array',
returnValue: 'resultRows'
});
wPost('query-result',rc);
break;
}
case 'vfs-remove':
poolUtil.removeVfs().then(()=>wPost('vfs-removed'));
break;
case 'vfs-pause':
poolUtil.pauseVfs();
wPost('vfs-paused');
break;
}
};
const hasOpfs = ()=>{
return globalThis.FileSystemHandle
&& globalThis.FileSystemDirectoryHandle
&& globalThis.FileSystemFileHandle
&& globalThis.FileSystemFileHandle.prototype.createSyncAccessHandle
&& navigator?.storage?.getDirectory;
};
if( !hasOpfs() ){
wPost('error',"OPFS not detected");
}else{
globalThis.sqlite3InitModule().then(async function(sqlite3){
S = sqlite3;
capi = S.capi;
wasm = S.wasm;
log("sqlite3 version:",capi.sqlite3_libversion(),
capi.sqlite3_sourceid());
//return sqlite3.installOpfsSAHPoolVfs(sahPoolConfig).then(pu=>poolUtil=pu);
}).then(()=>{
wPost('initialized');
});
}

View File

@ -1,5 +1,5 @@
C configure:\swhen\srunning\sproj-check-function-in-lib,\sstrip\s-Werror\sfrom\sCFLAGS\sfor\sthe\sduration\sof\sthe\stest.\sThis\senables\sCFLAGS='-Wall\s-Werror'\sand\sthe\slike\sto\sbe\spassed\sto\sconfigure\swithout\sbreaking\sthese\sconfigure-time\schecks.
D 2025-02-20T03:27:47.397
C Add\sthe\spause/unpause\scapability\sto\sthe\sopfs-sahpool\sVFS,\sas\sdiscussed\sin\s[forum:fe8cdb8431c|forum\sthread\sfe8cdb8431c].\sSummary:\sthis\sgives\sclients\sa\sway\sto\seke\ssome\sdegree\sof\smulti-page/tab/Worker\sconcurrency\sout\sof\sthis\sVFS\sbut\srequires\sthat\scoordination\sto\sbe\simplemented\sclient-side,\se.g.\svia\sa\sSharedWorker\sor\sWebLocks.
D 2025-02-20T04:14:26.736
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md e108e1e69ae8e8a59e93c455654b8ac9356a11720d3345df2a4743e9590fb20d
@ -619,7 +619,7 @@ F ext/session/sqlite3session.c 52a680dbb03c4734748b215d95987fb4d95ab23baaf053a01
F ext/session/sqlite3session.h aa5de3ec8ef0e5313e9f65dafd69e8ba292d170f07b57da9200c040068dab061
F ext/session/test_session.c 12e0a2c15fd60f92da4bb29c697c9177ff0c0dbcdc5129a54c47e999f147937a
F ext/wasm/EXPORTED_FUNCTIONS.fiddle.in 27450c8b8c70875a260aca55435ec927068b34cef801a96205adb81bdcefc65c
F ext/wasm/GNUmakefile 06e0556e9840fd3d8870997025c2507ace9ba7bf11195f770295fc7947dfc1b5
F ext/wasm/GNUmakefile 7889f9c5bab69233a65109e2bd00d627cab7470060e1b13b37781ac1b127f6c6
F ext/wasm/README-dist.txt f01081a850ce38a56706af6b481e3a7878e24e42b314cfcd4b129f0f8427066a
F ext/wasm/README.md b89605f65661cf35bf034ff6d43e448cc169b8017fc105d498e33b81218b482c
F ext/wasm/SQLTester/GNUmakefile e0794f676d55819951bbfae45cc5e8d7818dc460492dc317ce7f0d2eca15caff
@ -645,11 +645,11 @@ F ext/wasm/api/sqlite3-api-worker1.c-pp.js f646a65257973b8c4481f8a6a216370b85644
F ext/wasm/api/sqlite3-license-version-header.js 0c807a421f0187e778dc1078f10d2994b915123c1223fe752b60afdcd1263f89
F ext/wasm/api/sqlite3-opfs-async-proxy.js 3774befd97cd1a5e2895c8225a894aad946848c6d9b4028acc988b5d123475af
F ext/wasm/api/sqlite3-vfs-helper.c-pp.js 3f828cc66758acb40e9c5b4dcfd87fd478a14c8fb7f0630264e6c7fa0e57515d
F ext/wasm/api/sqlite3-vfs-opfs-sahpool.c-pp.js bb5e96cd0fd6e1e54538256433f1c60a4e3095063c4d1a79a8a022fc59be9571
F ext/wasm/api/sqlite3-vfs-opfs-sahpool.c-pp.js ce59229719220d64f491081bb2b423cea61cff171abcc7ac4ffc2f3687d27788
F ext/wasm/api/sqlite3-vfs-opfs.c-pp.js 9b86ca2d8276cf919fbc9ba2a10e9786033b64f92c2db844d951804dee6c4b4e
F ext/wasm/api/sqlite3-vtab-helper.c-pp.js e809739d71e8b35dfe1b55d24d91f02d04239e6aef7ca1ea92a15a29e704f616
F ext/wasm/api/sqlite3-wasm.c 6f9d8529072d072359cd22dc5dfb0572c524684686569cfbd0f9640d7619fc10
F ext/wasm/api/sqlite3-worker1-promiser.c-pp.js 46f303ba8ddd1b2f0a391798837beddfa72e8c897038c8047eda49ce7d5ed46b
F ext/wasm/api/sqlite3-worker1-promiser.c-pp.js bc65debfe43b81fc39fb25c40ad0cc1946bd82580fbf644351107b544d6177ee
F ext/wasm/api/sqlite3-worker1.c-pp.js 5e8706c2c4af2a57fbcdc02f4e7ef79869971bc21bb8ede777687786ce1c92d5
F ext/wasm/batch-runner-sahpool.html e9a38fdeb36a13eac7b50241dfe7ae066fe3f51f5c0b0151e7baee5fce0d07a7
F ext/wasm/batch-runner-sahpool.js 54a3ac228e6c4703fe72fb65c897e19156263a51fe9b7e21d2834a45e876aabd
@ -677,7 +677,7 @@ F ext/wasm/fiddle/fiddle-worker.js 850e66fce39b89d59e161d1abac43a181a4caa89ddeea
F ext/wasm/fiddle/fiddle.js b444a5646a9aac9f3fc06c53d78af5e1912eb235d69a8e6010723e4eb0e9d4a1
F ext/wasm/fiddle/index.html c79b1741cbeba78f88af0a84cf5ec7de87a909a6a8d10a369b1f4824c66c2088
F ext/wasm/index-dist.html 56132399702b15d70c474c3f1952541e25cb0922942868f70daf188f024b3730
F ext/wasm/index.html e4bbffdb3d40eff12b3f9c7abedef91787e2935620b7f8d40f2c774b80ad8fa9
F ext/wasm/index.html bcaa00eca521b372a6a62c7e7b17a870b0fcdf3e418a5921df1fd61e5344080d
F ext/wasm/jaccwabyt/jaccwabyt.js 1264710db3cfbcb6887d95665b7aeba60c1126eaef789ca4cf1a4a17d5bc7f54
F ext/wasm/jaccwabyt/jaccwabyt.md 59a20df389abcc3606eb4eaea7fb7ba14504beb3e345dbea9b99a0618ba3bec8
F ext/wasm/mkwasmbuilds.c baf6636e139e2c1e3b56e8dc26073ec80f6d14ae1876b023985315f43ccf312b
@ -696,10 +696,13 @@ F ext/wasm/test-opfs-vfs.html 1f2d672f3f3fce810dfd48a8d56914aba22e45c6834e262555
F ext/wasm/test-opfs-vfs.js 1618670e466f424aa289859fe0ec8ded223e42e9e69b5c851f809baaaca1a00c
F ext/wasm/tester1-worker.html ebc4b820a128963afce328ecf63ab200bd923309eb939f4110510ab449e9814c
F ext/wasm/tester1.c-pp.html 1c1bc78b858af2019e663b1a31e76657b73dc24bede28ca92fbe917c3a972af2
F ext/wasm/tester1.c-pp.js 05a0143c44a4114aad0ed40ce73c528febc3e0d6b69f48a51c895d7030015b74
F ext/wasm/tester1.c-pp.js f3a3cbf0207287c4caa9f84b4e2934804e4b5720c71eaf143f33c8122bd3c9f2
F ext/wasm/tests/opfs/concurrency/index.html 657578a6e9ce1e9b8be951549ed93a6a471f4520a99e5b545928668f4285fb5e
F ext/wasm/tests/opfs/concurrency/test.js d08889a5bb6e61937d0b8cbb78c9efbefbf65ad09f510589c779b7cc6a803a88
F ext/wasm/tests/opfs/concurrency/worker.js 0a8c1a3e6ebb38aabbee24f122693f1fb29d599948915c76906681bb7da1d3d2
F ext/wasm/tests/opfs/sahpool/index.html be736567fd92d3ecb9754c145755037cbbd2bca01385e2732294b53f4c842328
F ext/wasm/tests/opfs/sahpool/sahpool-pausing.js f264925cfc82155de38cecb3d204c36e0f6991460fff0cb7c15079454679a4e2
F ext/wasm/tests/opfs/sahpool/sahpool-worker.js bd25a43fc2ab2d1bafd8f2854ad3943ef673f7c3be03e95ecf1612ff6e8e2a61
F ext/wasm/wasmfs.make 68999f5bd8c489239592d59a420f8c627c99169bbd6fa16a404751f757b9f702
F magic.txt 5ade0bc977aa135e79e3faaea894d5671b26107cc91e70783aa7dc83f22f3ba0
F main.mk 323e71cd79e49c9e33d39b6558694a71c08973d9ac3ee4f211aa32e58bd876f5
@ -2207,8 +2210,9 @@ F tool/version-info.c 3b36468a90faf1bbd59c65fd0eb66522d9f941eedd364fabccd7227350
F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee87c1b31a7
F tool/warnings.sh 49a486c5069de041aedcbde4de178293e0463ae9918ecad7539eedf0ec77a139
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
P 628407f03d4bfb7499f0e6e2197089edf859380a3c4e6fecc517390327718141
R 0566dfd2338327afb5cc1d164e7ce94d
P 4ae9d6c642295e3a0c1732dacf7c18ecacd39d3e74e38381ac5531c8396f5f1c e205cdc468e02eefdeb8d391d921aa2d4d28a8b7b87036d6d937a9928261a413
R 224600d4f738e50568ab6b26e5819abb
T +closed e205cdc468e02eefdeb8d391d921aa2d4d28a8b7b87036d6d937a9928261a413 Closed\sby\sintegrate-merge.
U stephan
Z b470bbb1231ec7883dd8df58ba06834f
Z 8b67a5570124816c376abd5faa1b9b7a
# Remove this line to create a well-formed Fossil manifest.

View File

@ -1 +1 @@
4ae9d6c642295e3a0c1732dacf7c18ecacd39d3e74e38381ac5531c8396f5f1c
b5dbd521951e129b4dec69f191a872500dbf387b34a8479ad58b053ffcccbab9