"metrics for",self.location.href,":",metrics,
"\nTotal of",n,"op(s) for",t,
"ms (incl. "+w+" ms of waiting on the async side)");
- console.log("Serialization metrics:",JSON.stringify(metrics.s11n,0,2));
+ console.log("Serialization metrics:",metrics.s11n);
+ opRun('async-metrics');
},
reset: function(){
let k;
//].forEach((k)=>r(metrics[k] = Object.create(null)));
}
}/*metrics*/;
-
+ const promiseReject = function(err){
+ opfsVfs.dispose();
+ return promiseReject_(err);
+ };
+ const W = new Worker(options.proxyUri);
+ W._originalOnError = W.onerror /* will be restored later */;
+ W.onerror = function(err){
+ // The error object doesn't contain any useful info when the
+ // failure is, e.g., that the remote script is 404.
+ promiseReject(new Error("Loading OPFS async Worker failed for unknown reasons."));
+ };
const pDVfs = capi.sqlite3_vfs_find(null)/*pointer to default VFS*/;
const dVfs = pDVfs
? new sqlite3_vfs(pDVfs)
gets called at all in a wasm build, which is undefined).
*/
- const promiseReject = function(err){
- opfsVfs.dispose();
- return promiseReject_(err);
- };
- const W = new Worker(options.proxyUri);
- W._originalOnError = W.onerror /* will be restored later */;
- W.onerror = function(err){
- // The error object doesn't contain any useful info when the
- // failure is, e.g., that the remote script is 404.
- promiseReject(new Error("Loading OPFS async Worker failed for unknown reasons."));
- };
-
/**
State which we send to the async-api Worker or share with it.
This object must initially contain only cloneable or sharable
const state = Object.create(null);
state.verbose = options.verbose;
state.littleEndian = true;
- /** If true, the async counterpart should log exceptions to
+ /** Whether the async counterpart should log exceptions to
the serialization channel. That produces a great deal of
noise for seemingly innocuous things like xAccess() checks
- for missing files. */
- state.asyncS11nExceptions = false;
+ for missing files, so this option may have one of 3 values:
+
+ 0 = no exception logging
+
+ 1 = only log exceptions for "significant" ops like xOpen(),
+ xRead(), and xWrite().
+
+ 2 = log all exceptions.
+ */
+ state.asyncS11nExceptions = 1;
/* Size of file I/O buffer block. 64k = max sqlite3 page size. */
state.fileBufferSize =
1024 * 64;
state.opIds.xClose = i++;
state.opIds.xDelete = i++;
state.opIds.xDeleteNoWait = i++;
+ state.opIds.xFileControl = i++;
state.opIds.xFileSize = i++;
state.opIds.xOpen = i++;
state.opIds.xRead = i++;
state.opIds.xTruncate = i++;
state.opIds.xWrite = i++;
state.opIds.mkdir = i++;
- state.opIds.xFileControl = i++;
+ state.opIds['async-metrics'] = i++;
state.sabOP = new SharedArrayBuffer(i * 4/*sizeof int32*/);
opfsUtil.metrics.reset();
}
defaulting to 16.
*/
opfsUtil.randomFilename = randomFilename;
+
+ /**
+ Re-registers the OPFS VFS. This is intended only for odd use
+ cases which have to call sqlite3_shutdown() as part of their
+ initialization process, which will unregister the VFS
+ registered by installOpfsVfs(). If passed a truthy value, the
+ OPFS VFS is registered as the default VFS, else it is not made
+ the default. Returns the result of the the
+ sqlite3_vfs_register() call.
+
+ Design note: the problem of having to re-register things after
+ a shutdown/initialize pair is more general. How to best plug
+ that in to the library is unclear. In particular, we cannot
+ hook in to any C-side calls to sqlite3_initialize(), so we
+ cannot add an after-initialize callback mechanism.
+ */
+ opfsUtil.reregisterVfs = (asDefault=false)=>{
+ return capi.wasm.exports.sqlite3_vfs_register(
+ opfsVfs.pointer, asDefault ? 1 : 0
+ );
+ };
if(sqlite3.oo1){
opfsUtil.OpfsDb = function(...args){
wasm.scopedAllocPop(scope);
}
}/*sanityCheck()*/;
-
W.onmessage = function({data}){
//log("Worker.onmessage:",data);
}
console.log(self.location.href,
"metrics for",self.location.href,":\n",
- JSON.stringify(metrics,0,2)
- /*dev console can't expand this object!*/,
+ metrics,
"\nTotal of",n,"op(s) for",t,"ms",
"approx",w,"ms spent waiting on OPFS APIs.");
- console.log("Serialization metrics:",JSON.stringify(metrics.s11n,0,2));
+ console.log("Serialization metrics:",metrics.s11n);
};
warn("This file is very much experimental and under construction.",
truthy then each directory element leading to the file is created
along the way. Throws if any creation or resolution fails.
*/
-const getDirForPath = async function f(absFilename, createDirs = false){
+const getDirForFilename = async function f(absFilename, createDirs = false){
const path = getResolvedPath(absFilename, true);
const filename = path.pop();
let dh = state.rootDir;
to simplify finding them.
*/
const vfsAsyncImpls = {
- mkdir: async function(dirname){
+ 'async-metrics': async ()=>{
+ mTimeStart('async-metrics');
+ metrics.dump();
+ storeAndNotify('async-metrics', 0);
+ mTimeEnd();
+ },
+ mkdir: async (dirname)=>{
mTimeStart('mkdir');
let rc = 0;
wTimeStart('mkdir');
try {
- await getDirForPath(dirname+"/filepart", true);
+ await getDirForFilename(dirname+"/filepart", true);
}catch(e){
- state.s11n.storeException(e);
+ state.s11n.storeException(2,e);
rc = state.sq3Codes.SQLITE_IOERR;
}finally{
wTimeEnd();
storeAndNotify('mkdir', rc);
mTimeEnd();
},
- xAccess: async function(filename){
+ xAccess: async (filename)=>{
mTimeStart('xAccess');
/* OPFS cannot support the full range of xAccess() queries sqlite3
calls for. We can essentially just tell if the file is
let rc = 0;
wTimeStart('xAccess');
try{
- const [dh, fn] = await getDirForPath(filename);
+ const [dh, fn] = await getDirForFilename(filename);
await dh.getFileHandle(fn);
}catch(e){
- state.s11n.storeException(e);
+ state.s11n.storeException(2,e);
rc = state.sq3Codes.SQLITE_IOERR;
}finally{
wTimeEnd();
wTimeStart('xDelete');
try {
while(filename){
- const [hDir, filenamePart] = await getDirForPath(filename, false);
+ const [hDir, filenamePart] = await getDirForFilename(filename, false);
if(!filenamePart) break;
await hDir.removeEntry(filenamePart, {recursive});
if(0x1234 !== syncDir) break;
filename = filename.join('/');
}
}catch(e){
- state.s11n.storeException(e);
+ state.s11n.storeException(2,e);
rc = state.sq3Codes.SQLITE_IOERR_DELETE;
}
wTimeEnd();
state.s11n.serialize(Number(sz));
sz = 0;
}catch(e){
- state.s11n.storeException(e);
+ state.s11n.storeException(2,e);
sz = state.sq3Codes.SQLITE_IOERR;
}
wTimeEnd();
try{
let hDir, filenamePart;
try {
- [hDir, filenamePart] = await getDirForPath(filename, !!create);
+ [hDir, filenamePart] = await getDirForFilename(filename, !!create);
}catch(e){
storeAndNotify(opName, state.sql3Codes.SQLITE_NOTFOUND);
mTimeEnd();
}catch(e){
wTimeEnd();
error(opName,e);
- state.s11n.storeException(e);
+ state.s11n.storeException(1,e);
storeAndNotify(opName, state.sq3Codes.SQLITE_IOERR);
}
mTimeEnd();
}
}catch(e){
error("xRead() failed",e,fh);
- state.s11n.storeException(e);
+ state.s11n.storeException(1,e);
rc = state.sq3Codes.SQLITE_IOERR_READ;
}
storeAndNotify('xRead',rc);
wTimeStart('xSync');
await fh.accessHandle.flush();
}catch(e){
- state.s11n.storeException(e);
+ state.s11n.storeException(2,e);
}finally{
wTimeEnd();
}
await fh.accessHandle.truncate(size);
}catch(e){
error("xTruncate():",e,fh);
- state.s11n.storeException(e);
+ state.s11n.storeException(2,e);
rc = state.sq3Codes.SQLITE_IOERR_TRUNCATE;
}
wTimeEnd();
) ? 0 : state.sq3Codes.SQLITE_IOERR_WRITE;
}catch(e){
error("xWrite():",e,fh);
- state.s11n.storeException(e);
+ state.s11n.storeException(1,e);
rc = state.sq3Codes.SQLITE_IOERR_WRITE;
}finally{
wTimeEnd();
};
state.s11n.storeException = state.asyncS11nExceptions
- ? ((e)=>state.s11n.serialize(e.message))
+ ? ((priority,e)=>{
+ if(priority<=state.asyncS11nExceptions){
+ state.s11n.serialize(e.message);
+ }
+ })
: ()=>{};
return state.s11n;
const o = Object.create(null);
opHandlers[state.opIds[k]] = o;
o.key = k;
- o.f = vi;// || toss("No vfsAsyncImpls[",k,"]");
+ o.f = vi || toss("No vfsAsyncImpls[",k,"]");
}
- let metricsTimer = self.location.port>=1024 ? performance.now() : 0;
- // ^^^ in dev environment, dump out these metrics one time after a delay.
while(true){
try {
if('timed-out'===Atomics.wait(state.sabOPView, state.opIds.whichOp, 0, 500)){
const opId = Atomics.load(state.sabOPView, state.opIds.whichOp);
Atomics.store(state.sabOPView, state.opIds.whichOp, 0);
const hnd = opHandlers[opId] ?? toss("No waitLoop handler for whichOp #",opId);
- const args = state.s11n.deserialize();
+ const args = state.s11n.deserialize() || [];
state.s11n.serialize()/* clear s11n to keep the caller from
confusing this with an exception string
written by the upcoming operation */;
else error("Missing callback for opId",opId);
}catch(e){
error('in waitLoop():',e.message);
- }finally{
- // We can't call metrics.dump() from the dev console because this
- // thread is continually tied up in Atomics.wait(), so let's
- // do, for dev purposes only, a dump one time after 60 seconds.
- if(metricsTimer && (performance.now() > metricsTimer + 60000)){
- metrics.dump();
- metricsTimer = 0;
- }
}
};
};
-C Refactoring\stowards\sgetting\sfiddle\sto\ssupport\sOPFS\sas\sa\sfirst-class\scitizen.\sCertain\soperations,\se.g.\simport,\sexport,\sand\sunlink,\sare\snot\sOPFS-aware.
-D 2022-09-24T07:36:45.332
+C Reworked\sout\sthe\sOPFS\sasync\sproxy\smetrics\sare\sfetched\sso\sthat\sthey\splay\smore\snicely\swith\sthe\stight\sevent-polling\sloop.
+D 2022-09-24T10:12:19.409
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
F ext/wasm/api/sqlite3-api-cleanup.js 8564a6077cdcaea9a9f428a019af8a05887f0131e6a2a1e72a7ff1145fadfe77
F ext/wasm/api/sqlite3-api-glue.js cfff894bdf98a6c579975d09dd45471b0e3399f08a6f9e44a22646e8403196ed
F ext/wasm/api/sqlite3-api-oo1.js f974e79d9af8f26bf33928c5730b0988cc706d14f59a5fe36394739b92249841
-F ext/wasm/api/sqlite3-api-opfs.js d623ea3519cd81fe18e243adfdd07cd1fa4b07ff3b0fd0d2b269beb0e127acb3
+F ext/wasm/api/sqlite3-api-opfs.js 5585dc80aea9df54c3d5d3a6c62771bf741f21b23706330ba62571c57ec07abf
F ext/wasm/api/sqlite3-api-prologue.js a50ba8618e81a10a4fecd70f8723a7295cfcc0babd6df1dd018e7c5db2904aac
F ext/wasm/api/sqlite3-api-worker1.js 2eeb2a24e1a90322d84a9b88a99919b806623de62792436446099c0988f2030b
F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9
F ext/wasm/split-speedtest1-script.sh a3e271938d4d14ee49105eb05567c6a69ba4c1f1293583ad5af0cd3a3779e205 x
F ext/wasm/sql/000-mandelbrot.sql 775337a4b80938ac8146aedf88808282f04d02d983d82675bd63d9c2d97a15f0
F ext/wasm/sql/001-sudoku.sql 35b7cb7239ba5d5f193bc05ec379bcf66891bce6f2a5b3879f2f78d0917299b5
-F ext/wasm/sqlite3-opfs-async-proxy.js 483b6326e268589ed3749a4d1a60b4ec2afa8ff7c218a09d39430e3bde89862e
+F ext/wasm/sqlite3-opfs-async-proxy.js fe4b8268eea9acaec633ebd1dd3f85dae7c461c5c68985ab1075d9560b1db8e8
F ext/wasm/sqlite3-worker1-promiser.js 4fd0465688a28a75f1d4ee4406540ba494f49844e3cad0670d0437a001943365
F ext/wasm/sqlite3-worker1.js 0c1e7626304543969c3846573e080c082bf43bcaa47e87d416458af84f340a9e
F ext/wasm/test-opfs-vfs.html eb69dda21eb414b8f5e3f7c1cc0f774103cc9c0f87b2d28a33419e778abfbab5
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P e1249369d5ec1c582c280b1f578b35d53637fdf1cbd97c16d5ed95b136b83e56
-R bfb613620806c41d29eb36b1d39427ef
+P 1b923ed6438d7fef4508936e0c4bc026a368721698b1539961e3fb3140a185cb
+R 2784cada1344182947069d02e01bbe66
U stephan
-Z 135731a0096fbab4e7ac0c6ece9bf8ed
+Z 8970d31318ed191d3ed86820eec133d3
# Remove this line to create a well-formed Fossil manifest.
-1b923ed6438d7fef4508936e0c4bc026a368721698b1539961e3fb3140a185cb
\ No newline at end of file
+ef503ced5c2ca842be9aea9ef13719a378ed3020e884032db09afee1b8eba0a1
\ No newline at end of file