Further refactoring of opfs-sahpool and start integrating it into tester1.c-pp.js.

FossilOrigin-Name: 91c789234963b660ae900f0738906b28a477993709e286d8125b2f4d6101601c
This commit is contained in:
stephan 2023-07-18 18:52:41 +00:00
parent d69e6e6efb
commit b0dd9d427f
7 changed files with 149 additions and 80 deletions

View File

@ -59,7 +59,7 @@ const toExportForESM =
initModuleState.sqlite3Dir = li.join('/') + '/';
}
globalThis.sqlite3InitModule = function ff(...args){
globalThis.sqlite3InitModule = async function ff(...args){
//console.warn("Using replaced sqlite3InitModule()",globalThis.location);
return originalInit(...args).then((EmscriptenModule)=>{
if('undefined'!==typeof WorkerGlobalScope &&

View File

@ -1876,26 +1876,28 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
then it must be called by client-level code, which must not use
the library until the returned promise resolves.
Bug: if called while a prior call is still resolving, the 2nd
call will resolve prematurely, before the 1st call has finished
resolving. The current build setup precludes that possibility,
so it's only a hypothetical problem if/when this function
ever needs to be invoked by clients.
If called multiple times it will return the same promise on
subsequent calls. The current build setup precludes that
possibility, so it's only a hypothetical problem if/when this
function ever needs to be invoked by clients.
In Emscripten-based builds, this function is called
automatically and deleted from this object.
*/
asyncPostInit: async function(){
asyncPostInit: function ff(){
if(ff.ready instanceof Promise) return ff.ready;
let lip = sqlite3ApiBootstrap.initializersAsync;
delete sqlite3ApiBootstrap.initializersAsync;
if(!lip || !lip.length) return Promise.resolve(sqlite3);
if(!lip || !lip.length){
return ff.ready = Promise.resolve(sqlite3);
}
lip = lip.map((f)=>{
const p = (f instanceof Promise) ? f : f(sqlite3);
return p.catch((e)=>{
console.error("an async sqlite3 initializer failed:",e);
throw e;
});
return (f instanceof Promise) ? f : f(sqlite3);
});
const catcher = (e)=>{
config.error("an async sqlite3 initializer failed:",e);
throw e;
};
const postInit = ()=>{
if(!sqlite3.__isUnderTest){
/* Delete references to internal-only APIs which are used by
@ -1911,16 +1913,16 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
return sqlite3;
};
if(1){
/* Run all initializers in sequence. The advantage is that it
allows us to have post-init cleanup defined outside of this
routine at the end of the list and have it run at a
well-defined time. */
/* Run all initializers in the sequence they were added. The
advantage is that it allows us to have post-init cleanup
defined outside of this routine at the end of the list and
have it run at a well-defined time. */
let p = lip.shift();
while(lip.length) p = p.then(lip.shift());
return p.then(postInit);
return ff.ready = p.then(postInit).catch(catcher);
}else{
/* Run them in an arbitrary order. */
return Promise.all(lip).then(postInit);
return ff.ready = Promise.all(lip).then(postInit).catch(catcher);
}
},
/**

View File

@ -52,7 +52,7 @@
is not detected, the VFS is not registered.
*/
'use strict';
globalThis.sqlite3ApiBootstrap.initializersAsync.push(async function(sqlite3){
globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
const toss = sqlite3.util.toss;
const toss3 = sqlite3.util.toss3;
const initPromises = Object.create(null);
@ -400,46 +400,47 @@ globalThis.sqlite3ApiBootstrap.initializersAsync.push(async function(sqlite3){
/**
A SAHPoolUtil instance is exposed to clients in order to
manipulate an OpfsSAHPool object without directly exposing that
A SAHPoolUtil instance is exposed to clients in order to manipulate an OpfsSAHPool object without directly exposing that
object and allowing for some semantic changes compared to that
class.
Class docs are in the client-level docs for installOpfsSAHPoolVfs().
*/
class SAHPoolUtil {
/* This object's associated OpfsSAHPool. */
#p;
constructor(sahPool){
/* TODO: move the this-to-sahPool mapping into an external
WeakMap so as to not expose it to downstream clients. */
this.$p = sahPool;
this.#p = sahPool;
this.vfsName = sahPool.vfsName;
}
addCapacity = async function(n){
return this.$p.addCapacity(n);
async addCapacity(n){
return this.#p.addCapacity(n);
}
reduceCapacity = async function(n){
return this.$p.reduceCapacity(n);
async reduceCapacity(n){
return this.#p.reduceCapacity(n);
}
getCapacity = function(){
return this.$p.getCapacity(this.$p);
getCapacity(){
return this.#p.getCapacity(this.#p);
}
getActiveFileCount = function(){
return this.$p.getFileCount();
getActiveFileCount(){
return this.#p.getFileCount();
}
reserveMinimumCapacity = async function(min){
const c = this.$p.getCapacity();
return (c < min) ? this.$p.addCapacity(min - c) : c;
async reserveMinimumCapacity(min){
const c = this.#p.getCapacity();
return (c < min) ? this.#p.addCapacity(min - c) : c;
}
exportFile = function(name){
const sah = this.$p.mapFilenameToSAH.get(name) || toss("File not found:",name);
exportFile(name){
const sah = this.#p.mapFilenameToSAH.get(name) || toss("File not found:",name);
const n = sah.getSize() - HEADER_OFFSET_DATA;
const b = new Uint8Array(n>=0 ? n : 0);
if(n>0) sah.read(b, {at: HEADER_OFFSET_DATA});
return b;
}
importDb = function(name, bytes){
importDb(name, bytes){
const n = bytes.byteLength;
if(n<512 || n%512!=0){
toss("Byte array size is invalid for an SQLite db.");
@ -450,35 +451,33 @@ globalThis.sqlite3ApiBootstrap.initializersAsync.push(async function(sqlite3){
toss("Input does not contain an SQLite database header.");
}
}
const sah = this.$p.mapFilenameToSAH.get(name)
|| this.$p.nextAvailableSAH()
const sah = this.#p.mapFilenameToSAH.get(name)
|| this.#p.nextAvailableSAH()
|| toss("No available handles to import to.");
sah.write(bytes, {at: HEADER_OFFSET_DATA});
this.$p.setAssociatedPath(sah, name, capi.SQLITE_OPEN_MAIN_DB);
this.#p.setAssociatedPath(sah, name, capi.SQLITE_OPEN_MAIN_DB);
}
wipeFiles = async function(){
return this.$p.reset(true);
async wipeFiles(){return this.#p.reset(true)}
unlink(filename){
return this.#p.deletePath(filename);
}
unlink = function(filename){
return this.$p.deletePath(filename);
}
removeVfs = async function(){
if(!this.$p.cVfs.pointer) return false;
capi.sqlite3_vfs_unregister(this.$p.cVfs.pointer);
this.$p.cVfs.dispose();
async removeVfs(){
if(!this.#p.cVfs.pointer) return false;
capi.sqlite3_vfs_unregister(this.#p.cVfs.pointer);
this.#p.cVfs.dispose();
try{
this.$p.releaseAccessHandles();
if(this.$p.parentDirHandle){
await this.$p.parentDirHandle.removeEntry(
this.$p.dirHandle.name, {recursive: true}
this.#p.releaseAccessHandles();
if(this.#p.parentDirHandle){
await this.#p.parentDirHandle.removeEntry(
this.#p.dirHandle.name, {recursive: true}
);
this.$p.dirHandle = this.$p.parentDirHandle = undefined;
this.#p.dirHandle = this.#p.parentDirHandle = undefined;
}
}catch(e){
console.error(this.$p.vfsName,"removeVfs() failed:",e);
sqlite3.config.error(this.#p.vfsName,"removeVfs() failed:",e);
/*otherwise ignored - there is no recovery strategy*/
}
return true;
@ -679,7 +678,7 @@ globalThis.sqlite3ApiBootstrap.initializersAsync.push(async function(sqlite3){
throw new Error("Just testing rejection.");
}
if(initPromises[vfsName]){
//console.warn("Returning same OpfsSAHPool result",vfsName,initPromises[vfsName]);
console.warn("Returning same OpfsSAHPool result",options,vfsName,initPromises[vfsName]);
return initPromises[vfsName];
}
if(!globalThis.FileSystemHandle ||
@ -743,6 +742,9 @@ globalThis.sqlite3ApiBootstrap.initializersAsync.push(async function(sqlite3){
ensues.
*/
return initPromises[vfsName] = apiVersionCheck().then(async function(){
if(options.$testThrowInInit){
throw options.$testThrowInInit;
}
const thePool = new OpfsSAHPool(opfsVfs, options);
return thePool.isReady.then(async()=>{
/**
@ -1025,4 +1027,4 @@ globalThis.sqlite3ApiBootstrap.initializersAsync.push(async function(sqlite3){
});
}).catch(promiseReject);
}/*installOpfsSAHPoolVfs()*/;
}/*sqlite3ApiBootstrap.initializersAsync*/);
}/*sqlite3ApiBootstrap.initializers*/);

View File

@ -23,7 +23,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
installOpfsVfs() returns a Promise which, on success, installs an
sqlite3_vfs named "opfs", suitable for use with all sqlite3 APIs
which accept a VFS. It is intended to be called via
sqlite3ApiBootstrap.initializersAsync or an equivalent mechanism.
sqlite3ApiBootstrap.initializers or an equivalent mechanism.
The installed VFS uses the Origin-Private FileSystem API for
all file storage. On error it is rejected with an exception

View File

@ -65,6 +65,14 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
const haveWasmCTests = ()=>{
return !!wasm.exports.sqlite3_wasm_test_intptr;
};
const hasOpfs = ()=>{
return globalThis.FileSystemHandle
&& globalThis.FileSystemDirectoryHandle
&& globalThis.FileSystemFileHandle
&& globalThis.FileSystemFileHandle.prototype.createSyncAccessHandle
&& navigator?.storage?.getDirectory;
};
{
const mapToString = (v)=>{
switch(typeof v){
@ -277,7 +285,14 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
}
}
const tc = TestUtil.counter, now = performance.now();
await t.test.call(groupState, sqlite3);
let rc = t.test.call(groupState, sqlite3);
/*if(rc instanceof Promise){
rc = rc.catch((e)=>{
error("Test failure:",e);
throw e;
});
}*/
await rc;
const then = performance.now();
runtime += then - now;
logClass('faded',indent, indent,
@ -339,6 +354,11 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
T.g = T.addGroup;
T.t = T.addTest;
let capi, wasm/*assigned after module init*/;
const sahPoolConfig = {
name: 'opfs-sahpool-tester1',
clearOnInit: true,
initialCapacity: 3
};
////////////////////////////////////////////////////////////////////////
// End of infrastructure setup. Now define the tests...
////////////////////////////////////////////////////////////////////////
@ -1288,7 +1308,6 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
if(1){
const vfsList = capi.sqlite3_js_vfs_list();
T.assert(vfsList.length>1);
//log("vfsList =",vfsList);
wasm.scopedAllocCall(()=>{
const vfsArg = (v)=>wasm.xWrap.testConvertArg('sqlite3_vfs*',v);
for(const v of vfsList){
@ -2617,8 +2636,8 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
////////////////////////////////////////////////////////////////////////
T.g('OPFS: Origin-Private File System',
(sqlite3)=>(sqlite3.opfs
? true : "requires Worker thread in a compatible browser"))
(sqlite3)=>(sqlite3.capi.sqlite3_vfs_find("opfs")
|| 'requires "opfs" VFS'))
.t({
name: 'OPFS db sanity checks',
test: async function(sqlite3){
@ -2737,6 +2756,48 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
}/*OPFS util sanity checks*/)
;/* end OPFS tests */
////////////////////////////////////////////////////////////////////////
T.g('OPFS SyncAccessHandle Pool VFS',
(sqlite3)=>(hasOpfs() || "requires OPFS APIs"))
.t({
name: 'SAH sanity checks',
test: async function(sqlite3){
T.assert(!sqlite3.capi.sqlite3_vfs_find(sahPoolConfig.name))
.assert(sqlite3.capi.sqlite3_js_vfs_list().indexOf(sahPoolConfig.name) < 0)
const inst = sqlite3.installOpfsSAHPoolVfs,
catcher = (e)=>{
error("Cannot load SAH pool VFS.",
"This might not be a problem,",
"depending on the environment.");
return false;
};
let u1, u2;
const P1 = inst(sahPoolConfig).then(u=>u1 = u).catch(catcher),
P2 = inst(sahPoolConfig).then(u=>u2 = u).catch(catcher);
await Promise.all([P1, P2]);
if(!P1) return;
T.assert(u1 === u2)
.assert(sahPoolConfig.name === u1.vfsName)
.assert(sqlite3.capi.sqlite3_vfs_find(sahPoolConfig.name))
.assert(u1.getCapacity() === sahPoolConfig.initialCapacity)
.assert(5 === (await u2.addCapacity(2)))
.assert(sqlite3.capi.sqlite3_js_vfs_list().indexOf(sahPoolConfig.name) >= 0)
.assert(true === await u2.removeVfs())
.assert(false === await u1.removeVfs())
.assert(!sqlite3.capi.sqlite3_vfs_find(sahPoolConfig.name));
let cErr, u3;
const conf2 = JSON.parse(JSON.stringify(sahPoolConfig));
conf2.$testThrowInInit = new Error("Testing throwing during init.");
conf2.name = sahPoolConfig.name+'-err';
const P3 = await inst(conf2).then(u=>u3 = u).catch((e)=>cErr=e);
T.assert(P3 === conf2.$testThrowInInit)
.assert(cErr === P3)
.assert(undefined === u3)
.assert(!sqlite3.capi.sqlite3_vfs_find(conf2.name));
}
}/*OPFS SAH Pool sanity checks*/)
////////////////////////////////////////////////////////////////////////
T.g('Hook APIs')
.t({
@ -2942,8 +3003,7 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
.assert( capi.sqlite3session_enable(pSession, -1) > 0 )
.assert(undefined === db1.selectValue('select a from t where rowid=2'));
}else{
warn("sqlite3session_enable() tests disabled due to unexpected results.",
"(Possibly a tester misunderstanding, as opposed to a bug.)");
warn("sqlite3session_enable() tests are currently disabled.");
}
let db1Count = db1.selectValue("select count(*) from t");
T.assert( db1Count === (testSessionEnable ? 2 : 3) );
@ -3088,11 +3148,15 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
globalThis.sqlite3InitModule({
print: log,
printErr: error
}).then(function(sqlite3){
//console.log('sqlite3 =',sqlite3);
}).then(async function(sqlite3){
log("Done initializing WASM/JS bits. Running tests...");
sqlite3.config.warn("Installing sqlite3 bits as global S for local dev/test purposes.");
globalThis.S = sqlite3;
/*await sqlite3.installOpfsSAHPoolVfs(sahPoolConfig)
.then((u)=>log("Loaded",u.vfsName,"VFS"))
.catch(e=>{
log("Cannot install OpfsSAHPool.",e);
});*/
capi = sqlite3.capi;
wasm = sqlite3.wasm;
log("sqlite3 version:",capi.sqlite3_libversion(),
@ -3107,6 +3171,7 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
}else{
logClass('warning',"sqlite3_wasm_test_...() APIs unavailable.");
}
log("registered vfs list =",capi.sqlite3_js_vfs_list());
TestUtil.runTests(sqlite3);
});
})(self);

View File

@ -1,5 +1,5 @@
C Major\srestructuring\sof\sthe\sopfs-sahpool\sbits\sto\sbetter\ssupport\smultiple\sinstances\sper\sapp\s(each\ssandboxed\sfrom\seach\sother).\sEliminate\sthe\spesky\spromise\sresolution\srace\scondition\swhen\stwo\ssuch\sinstances\sare\sloaded\sin\sparallel.
D 2023-07-18T16:24:51.703
C Further\srefactoring\sof\sopfs-sahpool\sand\sstart\sintegrating\sit\sinto\stester1.c-pp.js.
D 2023-07-18T18:52:41.004
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@ -489,7 +489,7 @@ F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api d6a5078f48a5301ed17b9a30331075d9b2
F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-see fb29e62082a658f0d81102488414d422c393c4b20cc2f685b216bc566237957b
F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287
F ext/wasm/api/README.md f6cec6b0ce122cdff9440b30a3132dea3665b5b7baace910b43cbccdaaa376b9
F ext/wasm/api/extern-post-js.c-pp.js 116749b7e55b7519129de06d3d353e19df68cfb24b12204aa4dc30c9a83023fe
F ext/wasm/api/extern-post-js.c-pp.js 80f288131f9f4486a66e79dbf42d4402dc23e3cb4ef605377ae69f0545a6b8e6
F ext/wasm/api/extern-pre-js.js cc61c09c7a24a07dbecb4c352453c3985170cec12b4e7e7e7a4d11d43c5c8f41
F ext/wasm/api/post-js-footer.js cd0a8ec768501d9bd45d325ab0442037fb0e33d1f3b4f08902f15c34720ee4a1
F ext/wasm/api/post-js-header.js 47b6b281f39ad59fa6e8b658308cd98ea292c286a68407b35ff3ed9cfd281a62
@ -497,13 +497,13 @@ F ext/wasm/api/pre-js.c-pp.js ad906703f7429590f2fbf5e6498513bf727a1a4f0ebfa057af
F ext/wasm/api/sqlite3-api-cleanup.js 23ceec5ef74a0e649b19694ca985fd89e335771e21f24f50df352a626a8c81bf
F ext/wasm/api/sqlite3-api-glue.js f1b2dcb944de5138bb5bd9a1559d2e76a4f3ec25260963d709e8237476688803
F ext/wasm/api/sqlite3-api-oo1.js 9678dc4d9a5d39632b6ffe6ea94a023119260815bf32f265bf5f6c36c9516db8
F ext/wasm/api/sqlite3-api-prologue.js f68e87edc049793c4ed46b0ec8f3a3d8013eeb3fd56481029dda916d4d5fa3a3
F ext/wasm/api/sqlite3-api-prologue.js d747cbb379e13881c9edf39dce019cbbbae860c456ababe9d550b4b27666a1c9
F ext/wasm/api/sqlite3-api-worker1.js 9f32af64df1a031071912eea7a201557fe39b1738645c0134562bb84e88e2fec
F ext/wasm/api/sqlite3-license-version-header.js 0c807a421f0187e778dc1078f10d2994b915123c1223fe752b60afdcd1263f89
F ext/wasm/api/sqlite3-opfs-async-proxy.js 8cf8a897726f14071fae6be6648125162b256dfb4f96555b865dbb7a6b65e379
F ext/wasm/api/sqlite3-v-helper.js fc9ed95433d943a65905d16b7ed51515ddb6667d2a2c5a711c7ce33b29d3be31
F ext/wasm/api/sqlite3-vfs-opfs-sahpool.js fc6d12298919652eacc6b51138011277be2598d60fbcb086049967621db74e2c
F ext/wasm/api/sqlite3-vfs-opfs.c-pp.js 842d55b35a871ee5483cc5e0cf067a968362b4d61321f08c71aab5505c72f556
F ext/wasm/api/sqlite3-vfs-opfs-sahpool.js cd4d26fd8bf6c6b5bcb1666e8447c5388eb10ebfee57684e3658ee995f5a9645
F ext/wasm/api/sqlite3-vfs-opfs.c-pp.js 4946af0d6fbd395aa39966562ca85900664605a5f0cc10fff50146dee527812c
F ext/wasm/api/sqlite3-wasm.c 8867f1d41c112fb4a2cfe22ff224eccaf309fcdea266cee0ec554f85db72ef0f
F ext/wasm/api/sqlite3-worker1-promiser.c-pp.js bc06df0d599e625bde6a10a394e326dc68da9ff07fa5404354580f81566e591f
F ext/wasm/api/sqlite3-worker1.c-pp.js da509469755035e919c015deea41b4514b5e84c12a1332e6cc8d42cb2cc1fb75
@ -549,7 +549,7 @@ F ext/wasm/test-opfs-vfs.html 1f2d672f3f3fce810dfd48a8d56914aba22e45c6834e262555
F ext/wasm/test-opfs-vfs.js f09266873e1a34d9bdb6d3981ec8c9e382f31f215c9fd2f9016d2394b8ae9b7b
F ext/wasm/tester1-worker.html ebc4b820a128963afce328ecf63ab200bd923309eb939f4110510ab449e9814c
F ext/wasm/tester1.c-pp.html 1c1bc78b858af2019e663b1a31e76657b73dc24bede28ca92fbe917c3a972af2
F ext/wasm/tester1.c-pp.js 4420eb97b6b4fc79e4e156b4b8010dd9f373365f4230dd76d823fb04ce28ffde
F ext/wasm/tester1.c-pp.js 76607c9821e7854c061121b89337ca244f683b3a16c5415d078d361a8ec9e22f
F ext/wasm/tests/opfs/concurrency/index.html 0802373d57034d51835ff6041cda438c7a982deea6079efd98098d3e42fbcbc1
F ext/wasm/tests/opfs/concurrency/test.js a98016113eaf71e81ddbf71655aa29b0fed9a8b79a3cdd3620d1658eb1cc9a5d
F ext/wasm/tests/opfs/concurrency/worker.js 0a8c1a3e6ebb38aabbee24f122693f1fb29d599948915c76906681bb7da1d3d2
@ -2044,8 +2044,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P d036eaf6ac60c576428db40f015733c5d5425f7d613194fd8d9d4d98659077c4
R f91cd88307b7706bc464075e9268cd3a
P 95e5fa498f71708caeb3394636c4853530a8b2d54406e503f32750732d6815d5
R b629bc29f8d41e1b5f3ab58dda955102
U stephan
Z 4affd9934809bcbe1d4716ea36d23534
Z c2f2230ed00c70ba745095d2d4043967
# Remove this line to create a well-formed Fossil manifest.

View File

@ -1 +1 @@
95e5fa498f71708caeb3394636c4853530a8b2d54406e503f32750732d6815d5
91c789234963b660ae900f0738906b28a477993709e286d8125b2f4d6101601c