mirror of
https://github.com/sqlite/sqlite.git
synced 2025-07-29 08:01:23 +03:00
OPFS VFS: further internal refactoring towards experimenting with a new comms model.
FossilOrigin-Name: 5ca412ced24b4e3af5f467e710a597ed440badf7b8335346aade11d3cad3d1a1
This commit is contained in:
@ -128,7 +128,7 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri)
|
|||||||
// failure is, e.g., that the remote script is 404.
|
// failure is, e.g., that the remote script is 404.
|
||||||
promiseReject(new Error("Loading OPFS async Worker failed for unknown reasons."));
|
promiseReject(new Error("Loading OPFS async Worker failed for unknown reasons."));
|
||||||
};
|
};
|
||||||
const wMsg = (type,payload)=>W.postMessage({type,payload});
|
const wMsg = (type,args)=>W.postMessage({type,args});
|
||||||
/**
|
/**
|
||||||
Generic utilities for working with OPFS. This will get filled out
|
Generic utilities for working with OPFS. This will get filled out
|
||||||
by the Promise setup and, on success, installed as sqlite3.opfs.
|
by the Promise setup and, on success, installed as sqlite3.opfs.
|
||||||
@ -186,36 +186,48 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri)
|
|||||||
require a bit of acrobatics but should be feasible.
|
require a bit of acrobatics but should be feasible.
|
||||||
*/
|
*/
|
||||||
const state = Object.create(null);
|
const state = Object.create(null);
|
||||||
|
state.littleEndian = true;
|
||||||
state.verbose = options.verbose;
|
state.verbose = options.verbose;
|
||||||
state.fileBufferSize =
|
state.fileBufferSize =
|
||||||
1024 * 64 /* size of aFileHandle.sab. 64k = max sqlite3 page
|
1024 * 64 /* size of aFileHandle.sab. 64k = max sqlite3 page
|
||||||
size. */;
|
size. */;
|
||||||
state.sabOffsetS11n = state.fileBufferSize;
|
state.sabS11nOffset = state.fileBufferSize;
|
||||||
|
state.sabS11nSize = 2048;
|
||||||
state.sabIO = new SharedArrayBuffer(
|
state.sabIO = new SharedArrayBuffer(
|
||||||
state.fileBufferSize
|
state.fileBufferSize
|
||||||
+ 4096/* arg/result serialization */
|
+ state.sabS11nSize/* arg/result serialization block */
|
||||||
+ 8 /* to be removed - porting crutch */
|
|
||||||
);
|
);
|
||||||
state.fbInt64Offset =
|
|
||||||
state.sabIO.byteLength - 8 /*spot in fileHandle.sab to store an int64 result.
|
|
||||||
to be removed. Porting crutch. */;
|
|
||||||
state.opIds = Object.create(null);
|
state.opIds = Object.create(null);
|
||||||
|
state.rcIds = Object.create(null);
|
||||||
const metrics = Object.create(null);
|
const metrics = Object.create(null);
|
||||||
{
|
{
|
||||||
let i = 0;
|
let i = 0;
|
||||||
|
state.opIds.whichOp = i++;
|
||||||
state.opIds.nothing = i++;
|
state.opIds.nothing = i++;
|
||||||
state.opIds.xAccess = i++;
|
state.opIds.xAccess = i++;
|
||||||
|
state.rcIds.xAccess = i++;
|
||||||
state.opIds.xClose = i++;
|
state.opIds.xClose = i++;
|
||||||
|
state.rcIds.xClose = i++;
|
||||||
state.opIds.xDelete = i++;
|
state.opIds.xDelete = i++;
|
||||||
|
state.rcIds.xDelete = i++;
|
||||||
state.opIds.xDeleteNoWait = i++;
|
state.opIds.xDeleteNoWait = i++;
|
||||||
|
state.rcIds.xDeleteNoWait = i++;
|
||||||
state.opIds.xFileSize = i++;
|
state.opIds.xFileSize = i++;
|
||||||
|
state.rcIds.xFileSize = i++;
|
||||||
state.opIds.xOpen = i++;
|
state.opIds.xOpen = i++;
|
||||||
|
state.rcIds.xOpen = i++;
|
||||||
state.opIds.xRead = i++;
|
state.opIds.xRead = i++;
|
||||||
|
state.rcIds.xRead = i++;
|
||||||
state.opIds.xSleep = i++;
|
state.opIds.xSleep = i++;
|
||||||
|
state.rcIds.xSleep = i++;
|
||||||
state.opIds.xSync = i++;
|
state.opIds.xSync = i++;
|
||||||
|
state.rcIds.xSync = i++;
|
||||||
state.opIds.xTruncate = i++;
|
state.opIds.xTruncate = i++;
|
||||||
|
state.rcIds.xTruncate = i++;
|
||||||
state.opIds.xWrite = i++;
|
state.opIds.xWrite = i++;
|
||||||
|
state.rcIds.xWrite = i++;
|
||||||
state.opIds.mkdir = i++;
|
state.opIds.mkdir = i++;
|
||||||
|
state.rcIds.mkdir = i++;
|
||||||
state.sabOP = new SharedArrayBuffer(i * 4/*sizeof int32*/);
|
state.sabOP = new SharedArrayBuffer(i * 4/*sizeof int32*/);
|
||||||
state.opIds.xFileControl = state.opIds.xSync /* special case */;
|
state.opIds.xFileControl = state.opIds.xSync /* special case */;
|
||||||
opfsUtil.metrics.reset();
|
opfsUtil.metrics.reset();
|
||||||
@ -247,7 +259,7 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri)
|
|||||||
must be a single object or primitive value, depending on the
|
must be a single object or primitive value, depending on the
|
||||||
given operation's signature in the async API counterpart.
|
given operation's signature in the async API counterpart.
|
||||||
*/
|
*/
|
||||||
const opRun = (op,args)=>{
|
const opRun = (op,...args)=>{
|
||||||
const t = performance.now();
|
const t = performance.now();
|
||||||
Atomics.store(state.sabOPView, state.opIds[op], -1);
|
Atomics.store(state.sabOPView, state.opIds[op], -1);
|
||||||
wMsg(op, args);
|
wMsg(op, args);
|
||||||
@ -256,6 +268,44 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri)
|
|||||||
return Atomics.load(state.sabOPView, state.opIds[op]);
|
return Atomics.load(state.sabOPView, state.opIds[op]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const initS11n = ()=>{
|
||||||
|
// Achtung: this code is 100% duplicated in the other half of this proxy!
|
||||||
|
if(state.s11n) return state.s11n;
|
||||||
|
const jsonDecoder = new TextDecoder(),
|
||||||
|
jsonEncoder = new TextEncoder('utf-8'),
|
||||||
|
viewSz = new DataView(state.sabIO, state.sabS11nOffset, 4),
|
||||||
|
viewJson = new Uint8Array(state.sabIO, state.sabS11nOffset+4, state.sabS11nSize-4);
|
||||||
|
state.s11n = Object.create(null);
|
||||||
|
/**
|
||||||
|
Returns an array of the state serialized by the most recent
|
||||||
|
serialize() operation (here or in the counterpart thread), or
|
||||||
|
null if the serialization buffer is empty.
|
||||||
|
*/
|
||||||
|
state.s11n.deserialize = function(){
|
||||||
|
const sz = viewSz.getInt32(0, state.littleEndian);
|
||||||
|
const json = sz ? jsonDecoder.decode(
|
||||||
|
viewJson.slice(0, sz)
|
||||||
|
/* slice() (copy) needed, instead of subarray() (reference),
|
||||||
|
because TextDecoder throws if asked to decode from an
|
||||||
|
SAB. */
|
||||||
|
) : null;
|
||||||
|
return JSON.parse(json);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
Serializes all arguments to the shared buffer for consumption
|
||||||
|
by the counterpart thread. This impl currently uses JSON for
|
||||||
|
serialization for simplicy of implementation, but if that
|
||||||
|
proves imperformant then a lower-level approach will be
|
||||||
|
created.
|
||||||
|
*/
|
||||||
|
state.s11n.serialize = function(...args){
|
||||||
|
const json = jsonEncoder.encode(JSON.stringify(args));
|
||||||
|
viewSz.setInt32(0, json.byteLength, state.littleEndian);
|
||||||
|
viewJson.set(json);
|
||||||
|
};
|
||||||
|
return state.s11n;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Generates a random ASCII string len characters long, intended for
|
Generates a random ASCII string len characters long, intended for
|
||||||
use as a temporary file name.
|
use as a temporary file name.
|
||||||
@ -425,7 +475,7 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri)
|
|||||||
xFileControl: function(pFile, opId, pArg){
|
xFileControl: function(pFile, opId, pArg){
|
||||||
mTimeStart('xFileControl');
|
mTimeStart('xFileControl');
|
||||||
const rc = (capi.SQLITE_FCNTL_SYNC===opId)
|
const rc = (capi.SQLITE_FCNTL_SYNC===opId)
|
||||||
? opRun('xSync', {fid:pFile, flags:0})
|
? opRun('xSync', pFile, 0)
|
||||||
: capi.SQLITE_NOTFOUND;
|
: capi.SQLITE_NOTFOUND;
|
||||||
mTimeEnd();
|
mTimeEnd();
|
||||||
return rc;
|
return rc;
|
||||||
@ -434,10 +484,8 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri)
|
|||||||
mTimeStart('xFileSize');
|
mTimeStart('xFileSize');
|
||||||
const rc = opRun('xFileSize', pFile);
|
const rc = opRun('xFileSize', pFile);
|
||||||
if(!isWorkerErrCode(rc)){
|
if(!isWorkerErrCode(rc)){
|
||||||
wasm.setMemValue(
|
const sz = state.s11n.deserialize()[0];
|
||||||
pSz64, __openFiles[pFile].sabViewFileSize.getBigInt64(0,true),
|
wasm.setMemValue(pSz64, BigInt(sz), 'i64');
|
||||||
'i64'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
mTimeEnd();
|
mTimeEnd();
|
||||||
return rc;
|
return rc;
|
||||||
@ -447,14 +495,13 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri)
|
|||||||
//warn("xLock(",arguments,") is a no-op");
|
//warn("xLock(",arguments,") is a no-op");
|
||||||
return 0;
|
return 0;
|
||||||
},
|
},
|
||||||
xRead: function(pFile,pDest,n,offset){
|
xRead: function(pFile,pDest,n,offset64){
|
||||||
/* int (*xRead)(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst) */
|
/* int (*xRead)(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst) */
|
||||||
mTimeStart('xRead');
|
mTimeStart('xRead');
|
||||||
const f = __openFiles[pFile];
|
const f = __openFiles[pFile];
|
||||||
let rc;
|
let rc;
|
||||||
try {
|
try {
|
||||||
// FIXME(?): block until we finish copying the xRead result buffer. How?
|
rc = opRun('xRead',pFile, n, Number(offset64));
|
||||||
rc = opRun('xRead',{fid:pFile, n, offset});
|
|
||||||
if(0===rc || capi.SQLITE_IOERR_SHORT_READ===rc){
|
if(0===rc || capi.SQLITE_IOERR_SHORT_READ===rc){
|
||||||
// set() seems to be the fastest way to copy this...
|
// set() seems to be the fastest way to copy this...
|
||||||
wasm.heap8u().set(f.sabView.subarray(0, n), pDest);
|
wasm.heap8u().set(f.sabView.subarray(0, n), pDest);
|
||||||
@ -468,11 +515,11 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri)
|
|||||||
},
|
},
|
||||||
xSync: function(pFile,flags){
|
xSync: function(pFile,flags){
|
||||||
++metrics.xSync.count;
|
++metrics.xSync.count;
|
||||||
return 0; // impl'd in xFileControl(). opRun('xSync', {fid:pFile, flags});
|
return 0; // impl'd in xFileControl()
|
||||||
},
|
},
|
||||||
xTruncate: function(pFile,sz64){
|
xTruncate: function(pFile,sz64){
|
||||||
mTimeStart('xTruncate');
|
mTimeStart('xTruncate');
|
||||||
const rc = opRun('xTruncate', {fid:pFile, size: sz64});
|
const rc = opRun('xTruncate', pFile, Number(sz64));
|
||||||
mTimeEnd();
|
mTimeEnd();
|
||||||
return rc;
|
return rc;
|
||||||
},
|
},
|
||||||
@ -481,15 +528,14 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri)
|
|||||||
//warn("xUnlock(",arguments,") is a no-op");
|
//warn("xUnlock(",arguments,") is a no-op");
|
||||||
return 0;
|
return 0;
|
||||||
},
|
},
|
||||||
xWrite: function(pFile,pSrc,n,offset){
|
xWrite: function(pFile,pSrc,n,offset64){
|
||||||
/* int (*xWrite)(sqlite3_file*, const void*, int iAmt, sqlite3_int64 iOfst) */
|
/* int (*xWrite)(sqlite3_file*, const void*, int iAmt, sqlite3_int64 iOfst) */
|
||||||
mTimeStart('xWrite');
|
mTimeStart('xWrite');
|
||||||
const f = __openFiles[pFile];
|
const f = __openFiles[pFile];
|
||||||
let rc;
|
let rc;
|
||||||
try {
|
try {
|
||||||
// FIXME(?): block from here until we finish the xWrite. How?
|
|
||||||
f.sabView.set(wasm.heap8u().subarray(pSrc, pSrc+n));
|
f.sabView.set(wasm.heap8u().subarray(pSrc, pSrc+n));
|
||||||
rc = opRun('xWrite',{fid:pFile, n, offset});
|
rc = opRun('xWrite', pFile, n, Number(offset64));
|
||||||
}catch(e){
|
}catch(e){
|
||||||
error("xWrite(",arguments,") failed:",e,f);
|
error("xWrite(",arguments,") failed:",e,f);
|
||||||
rc = capi.SQLITE_IOERR_WRITE;
|
rc = capi.SQLITE_IOERR_WRITE;
|
||||||
@ -527,7 +573,7 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri)
|
|||||||
},
|
},
|
||||||
xDelete: function(pVfs, zName, doSyncDir){
|
xDelete: function(pVfs, zName, doSyncDir){
|
||||||
mTimeStart('xDelete');
|
mTimeStart('xDelete');
|
||||||
opRun('xDelete', {filename: wasm.cstringToJs(zName), syncDir: doSyncDir});
|
opRun('xDelete', wasm.cstringToJs(zName), doSyncDir, false);
|
||||||
/* We're ignoring errors because we cannot yet differentiate
|
/* We're ignoring errors because we cannot yet differentiate
|
||||||
between harmless and non-harmless failures. */
|
between harmless and non-harmless failures. */
|
||||||
mTimeEnd();
|
mTimeEnd();
|
||||||
@ -577,24 +623,22 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri)
|
|||||||
}else if('number'===typeof zName){
|
}else if('number'===typeof zName){
|
||||||
zName = wasm.cstringToJs(zName);
|
zName = wasm.cstringToJs(zName);
|
||||||
}
|
}
|
||||||
const args = Object.create(null);
|
const fh = Object.create(null);
|
||||||
args.fid = pFile;
|
fh.fid = pFile;
|
||||||
args.filename = zName;
|
fh.filename = zName;
|
||||||
args.sab = new SharedArrayBuffer(state.fileBufferSize);
|
fh.sab = new SharedArrayBuffer(state.fileBufferSize);
|
||||||
args.flags = flags;
|
fh.flags = flags;
|
||||||
const rc = opRun('xOpen', args);
|
const rc = opRun('xOpen', pFile, zName, flags);
|
||||||
if(!rc){
|
if(!rc){
|
||||||
/* Recall that sqlite3_vfs::xClose() will be called, even on
|
/* Recall that sqlite3_vfs::xClose() will be called, even on
|
||||||
error, unless pFile->pMethods is NULL. */
|
error, unless pFile->pMethods is NULL. */
|
||||||
if(args.readOnly){
|
if(fh.readOnly){
|
||||||
wasm.setMemValue(pOutFlags, capi.SQLITE_OPEN_READONLY, 'i32');
|
wasm.setMemValue(pOutFlags, capi.SQLITE_OPEN_READONLY, 'i32');
|
||||||
}
|
}
|
||||||
__openFiles[pFile] = args;
|
__openFiles[pFile] = fh;
|
||||||
args.sabView = new Uint8Array(state.sabIO, 0, state.fileBufferSize);
|
fh.sabView = state.sabFileBufView;
|
||||||
args.sabViewFileSize = new DataView(state.sabIO, state.fbInt64Offset, 8);
|
fh.sq3File = new sqlite3_file(pFile);
|
||||||
args.sq3File = new sqlite3_file(pFile);
|
fh.sq3File.$pMethods = opfsIoMethods.pointer;
|
||||||
args.sq3File.$pMethods = opfsIoMethods.pointer;
|
|
||||||
args.ba = new Uint8Array(args.sab);
|
|
||||||
}
|
}
|
||||||
mTimeEnd();
|
mTimeEnd();
|
||||||
return rc;
|
return rc;
|
||||||
@ -640,13 +684,17 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri)
|
|||||||
but cannot report the nature of the failure.
|
but cannot report the nature of the failure.
|
||||||
*/
|
*/
|
||||||
opfsUtil.deleteEntry = function(fsEntryName,recursive=false){
|
opfsUtil.deleteEntry = function(fsEntryName,recursive=false){
|
||||||
return 0===opRun('xDelete', {filename:fsEntryName, recursive});
|
return 0===opRun('xDelete', fsEntryName, 0, recursive);
|
||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
Exactly like deleteEntry() but runs asynchronously.
|
Exactly like deleteEntry() but runs asynchronously. This is a
|
||||||
|
"fire and forget" operation: it does not return a promise
|
||||||
|
because the counterpart operation happens in another thread and
|
||||||
|
waiting on that result in a Promise would block the OPFS VFS
|
||||||
|
from acting until it completed.
|
||||||
*/
|
*/
|
||||||
opfsUtil.deleteEntryAsync = async function(fsEntryName,recursive=false){
|
opfsUtil.deleteEntryAsync = function(fsEntryName,recursive=false){
|
||||||
wMsg('xDeleteNoWait', {filename: fsEntryName, recursive});
|
wMsg('xDeleteNoWait', [fsEntryName, 0, recursive]);
|
||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
Synchronously creates the given directory name, recursively, in
|
Synchronously creates the given directory name, recursively, in
|
||||||
@ -753,11 +801,11 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri)
|
|||||||
W.onmessage = function({data}){
|
W.onmessage = function({data}){
|
||||||
//log("Worker.onmessage:",data);
|
//log("Worker.onmessage:",data);
|
||||||
switch(data.type){
|
switch(data.type){
|
||||||
case 'loaded':
|
case 'opfs-async-loaded':
|
||||||
/*Pass our config and shared state on to the async worker.*/
|
/*Pass our config and shared state on to the async worker.*/
|
||||||
wMsg('init',state);
|
wMsg('opfs-async-init',state);
|
||||||
break;
|
break;
|
||||||
case 'inited':{
|
case 'opfs-async-inited':{
|
||||||
/*Indicates that the async partner has received the 'init',
|
/*Indicates that the async partner has received the 'init',
|
||||||
so we now know that the state object is no longer subject to
|
so we now know that the state object is no longer subject to
|
||||||
being copied by a pending postMessage() call.*/
|
being copied by a pending postMessage() call.*/
|
||||||
@ -772,7 +820,9 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri)
|
|||||||
}
|
}
|
||||||
capi.sqlite3_vfs_register.addReference(opfsVfs, opfsIoMethods);
|
capi.sqlite3_vfs_register.addReference(opfsVfs, opfsIoMethods);
|
||||||
state.sabOPView = new Int32Array(state.sabOP);
|
state.sabOPView = new Int32Array(state.sabOP);
|
||||||
state.sabFileBufView = new Uint8Array(state.sabFileBufView, 0, state.fileBufferSize);
|
state.sabFileBufView = new Uint8Array(state.sabIO, 0, state.fileBufferSize);
|
||||||
|
state.sabS11nView = new Uint8Array(state.sabIO, state.sabS11nOffset, state.sabS11nSize);
|
||||||
|
initS11n();
|
||||||
if(options.sanityChecks){
|
if(options.sanityChecks){
|
||||||
warn("Running sanity checks because of opfs-sanity-check URL arg...");
|
warn("Running sanity checks because of opfs-sanity-check URL arg...");
|
||||||
sanityCheck();
|
sanityCheck();
|
||||||
|
@ -220,7 +220,7 @@ const vfsAsyncImpls = {
|
|||||||
storeAndNotify('xDelete', rc);
|
storeAndNotify('xDelete', rc);
|
||||||
mTimeEnd();
|
mTimeEnd();
|
||||||
},
|
},
|
||||||
xDeleteNoWait: async function({filename, syncDir, recursive = false}){
|
xDeleteNoWait: async function(filename, syncDir = 0, recursive = false){
|
||||||
/* The syncDir flag is, for purposes of the VFS API's semantics,
|
/* The syncDir flag is, for purposes of the VFS API's semantics,
|
||||||
ignored here. However, if it has the value 0x1234 then: after
|
ignored here. However, if it has the value 0x1234 then: after
|
||||||
deleting the given file, recursively try to delete any empty
|
deleting the given file, recursively try to delete any empty
|
||||||
@ -261,10 +261,7 @@ const vfsAsyncImpls = {
|
|||||||
let sz;
|
let sz;
|
||||||
try{
|
try{
|
||||||
sz = await fh.accessHandle.getSize();
|
sz = await fh.accessHandle.getSize();
|
||||||
if(!fh.sabViewFileSize){
|
state.s11n.serialize(Number(sz));
|
||||||
fh.sabViewFileSize = new DataView(state.sabIO,state.fbInt64Offset,8);
|
|
||||||
}
|
|
||||||
fh.sabViewFileSize.setBigInt64(0, BigInt(sz), true);
|
|
||||||
sz = 0;
|
sz = 0;
|
||||||
}catch(e){
|
}catch(e){
|
||||||
error("xFileSize():",e, fh);
|
error("xFileSize():",e, fh);
|
||||||
@ -273,11 +270,7 @@ const vfsAsyncImpls = {
|
|||||||
storeAndNotify('xFileSize', sz);
|
storeAndNotify('xFileSize', sz);
|
||||||
mTimeEnd();
|
mTimeEnd();
|
||||||
},
|
},
|
||||||
xOpen: async function({
|
xOpen: async function(fid/*sqlite3_file pointer*/, filename, flags){
|
||||||
fid/*sqlite3_file pointer*/,
|
|
||||||
filename,
|
|
||||||
flags
|
|
||||||
}){
|
|
||||||
const opName = 'xOpen';
|
const opName = 'xOpen';
|
||||||
mTimeStart(opName);
|
mTimeStart(opName);
|
||||||
log(opName+"(",arguments[0],")");
|
log(opName+"(",arguments[0],")");
|
||||||
@ -316,7 +309,7 @@ const vfsAsyncImpls = {
|
|||||||
}
|
}
|
||||||
mTimeEnd();
|
mTimeEnd();
|
||||||
},
|
},
|
||||||
xRead: async function({fid,n,offset}){
|
xRead: async function(fid,n,offset){
|
||||||
mTimeStart('xRead');
|
mTimeStart('xRead');
|
||||||
log("xRead(",arguments[0],")");
|
log("xRead(",arguments[0],")");
|
||||||
let rc = 0;
|
let rc = 0;
|
||||||
@ -337,7 +330,7 @@ const vfsAsyncImpls = {
|
|||||||
storeAndNotify('xRead',rc);
|
storeAndNotify('xRead',rc);
|
||||||
mTimeEnd();
|
mTimeEnd();
|
||||||
},
|
},
|
||||||
xSync: async function({fid,flags/*ignored*/}){
|
xSync: async function(fid,flags/*ignored*/){
|
||||||
mTimeStart('xSync');
|
mTimeStart('xSync');
|
||||||
log("xSync(",arguments[0],")");
|
log("xSync(",arguments[0],")");
|
||||||
const fh = __openFiles[fid];
|
const fh = __openFiles[fid];
|
||||||
@ -345,14 +338,14 @@ const vfsAsyncImpls = {
|
|||||||
storeAndNotify('xSync',0);
|
storeAndNotify('xSync',0);
|
||||||
mTimeEnd();
|
mTimeEnd();
|
||||||
},
|
},
|
||||||
xTruncate: async function({fid,size}){
|
xTruncate: async function(fid,size){
|
||||||
mTimeStart('xTruncate');
|
mTimeStart('xTruncate');
|
||||||
log("xTruncate(",arguments[0],")");
|
log("xTruncate(",arguments[0],")");
|
||||||
let rc = 0;
|
let rc = 0;
|
||||||
const fh = __openFiles[fid];
|
const fh = __openFiles[fid];
|
||||||
try{
|
try{
|
||||||
affirmNotRO('xTruncate', fh);
|
affirmNotRO('xTruncate', fh);
|
||||||
await fh.accessHandle.truncate(Number(size));
|
await fh.accessHandle.truncate(size);
|
||||||
}catch(e){
|
}catch(e){
|
||||||
error("xTruncate():",e,fh);
|
error("xTruncate():",e,fh);
|
||||||
rc = state.sq3Codes.SQLITE_IOERR_TRUNCATE;
|
rc = state.sq3Codes.SQLITE_IOERR_TRUNCATE;
|
||||||
@ -360,7 +353,7 @@ const vfsAsyncImpls = {
|
|||||||
storeAndNotify('xTruncate',rc);
|
storeAndNotify('xTruncate',rc);
|
||||||
mTimeEnd();
|
mTimeEnd();
|
||||||
},
|
},
|
||||||
xWrite: async function({fid,n,offset}){
|
xWrite: async function(fid,n,offset){
|
||||||
mTimeStart('xWrite');
|
mTimeStart('xWrite');
|
||||||
log("xWrite(",arguments[0],")");
|
log("xWrite(",arguments[0],")");
|
||||||
let rc;
|
let rc;
|
||||||
@ -380,6 +373,70 @@ const vfsAsyncImpls = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const initS11n = ()=>{
|
||||||
|
// Achtung: this code is 100% duplicated in the other half of this proxy!
|
||||||
|
if(state.s11n) return state.s11n;
|
||||||
|
const jsonDecoder = new TextDecoder(),
|
||||||
|
jsonEncoder = new TextEncoder('utf-8'),
|
||||||
|
viewSz = new DataView(state.sabIO, state.sabS11nOffset, 4),
|
||||||
|
viewJson = new Uint8Array(state.sabIO, state.sabS11nOffset+4, state.sabS11nSize-4);
|
||||||
|
state.s11n = Object.create(null);
|
||||||
|
/**
|
||||||
|
Returns an array of the state serialized by the most recent
|
||||||
|
serialize() operation (here or in the counterpart thread), or
|
||||||
|
null if the serialization buffer is empty.
|
||||||
|
*/
|
||||||
|
state.s11n.deserialize = function(){
|
||||||
|
const sz = viewSz.getInt32(0, state.littleEndian);
|
||||||
|
const json = sz ? jsonDecoder.decode(
|
||||||
|
viewJson.slice(0, sz)
|
||||||
|
/* slice() (copy) needed, instead of subarray() (reference),
|
||||||
|
because TextDecoder throws if asked to decode from an
|
||||||
|
SAB. */
|
||||||
|
) : null;
|
||||||
|
return JSON.parse(json);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
Serializes all arguments to the shared buffer for consumption
|
||||||
|
by the counterpart thread. This impl currently uses JSON for
|
||||||
|
serialization for simplicy of implementation, but if that
|
||||||
|
proves imperformant then a lower-level approach will be
|
||||||
|
created.
|
||||||
|
*/
|
||||||
|
state.s11n.serialize = function(...args){
|
||||||
|
const json = jsonEncoder.encode(JSON.stringify(args));
|
||||||
|
viewSz.setInt32(0, json.byteLength, state.littleEndian);
|
||||||
|
viewJson.set(json);
|
||||||
|
};
|
||||||
|
return state.s11n;
|
||||||
|
};
|
||||||
|
|
||||||
|
const waitLoop = function(){
|
||||||
|
const opHandlers = Object.create(null);
|
||||||
|
for(let k of Object.keys(state.opIds)){
|
||||||
|
const o = Object.create(null);
|
||||||
|
opHandlers[state.opIds[k]] = o;
|
||||||
|
o.key = k;
|
||||||
|
}
|
||||||
|
const sabOP = state.sabOP;
|
||||||
|
for(;;){
|
||||||
|
try {
|
||||||
|
Atomics.store(sabOP, state.opIds.whichOp, 0);
|
||||||
|
Atomic.wait(sabOP, state.opIds.whichOp);
|
||||||
|
const opId = Atomics.load(sabOP, state.opIds.whichOp);
|
||||||
|
const hnd = opHandlers[opId] ?? toss("No waitLoop handler for whichOp #",opId);
|
||||||
|
const args = state.s11n.deserialize();
|
||||||
|
log("whichOp =",opId,hnd,args);
|
||||||
|
const rc = 0/*TODO: run op*/;
|
||||||
|
Atomics.store(sabOP, state.rcIds[hnd.key], rc);
|
||||||
|
Atomics.notify(sabOP, state.rcIds[hnd.key]);
|
||||||
|
}catch(e){
|
||||||
|
error('in waitLoop():',e.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
navigator.storage.getDirectory().then(function(d){
|
navigator.storage.getDirectory().then(function(d){
|
||||||
const wMsg = (type)=>postMessage({type});
|
const wMsg = (type)=>postMessage({type});
|
||||||
state.rootDir = d;
|
state.rootDir = d;
|
||||||
@ -387,33 +444,38 @@ navigator.storage.getDirectory().then(function(d){
|
|||||||
self.onmessage = async function({data}){
|
self.onmessage = async function({data}){
|
||||||
log("self.onmessage()",data);
|
log("self.onmessage()",data);
|
||||||
switch(data.type){
|
switch(data.type){
|
||||||
case 'init':{
|
case 'opfs-async-init':{
|
||||||
/* Receive shared state from synchronous partner */
|
/* Receive shared state from synchronous partner */
|
||||||
const opt = data.payload;
|
const opt = data.args;
|
||||||
|
state.littleEndian = opt.littleEndian;
|
||||||
state.verbose = opt.verbose ?? 2;
|
state.verbose = opt.verbose ?? 2;
|
||||||
state.fileBufferSize = opt.fileBufferSize;
|
state.fileBufferSize = opt.fileBufferSize;
|
||||||
state.fbInt64Offset = opt.fbInt64Offset;
|
state.sabS11nOffset = opt.sabS11nOffset;
|
||||||
|
state.sabS11nSize = opt.sabS11nSize;
|
||||||
state.sabOP = opt.sabOP;
|
state.sabOP = opt.sabOP;
|
||||||
state.sabOPView = new Int32Array(state.sabOP);
|
state.sabOPView = new Int32Array(state.sabOP);
|
||||||
state.sabIO = opt.sabIO;
|
state.sabIO = opt.sabIO;
|
||||||
state.sabFileBufView = new Uint8Array(state.sabIO, 0, state.fileBufferSize);
|
state.sabFileBufView = new Uint8Array(state.sabIO, 0, state.fileBufferSize);
|
||||||
|
state.sabS11nView = new Uint8Array(state.sabIO, state.sabS11nOffset, state.sabS11nSize);
|
||||||
state.opIds = opt.opIds;
|
state.opIds = opt.opIds;
|
||||||
|
state.rcIds = opt.rcIds;
|
||||||
state.sq3Codes = opt.sq3Codes;
|
state.sq3Codes = opt.sq3Codes;
|
||||||
Object.keys(vfsAsyncImpls).forEach((k)=>{
|
Object.keys(vfsAsyncImpls).forEach((k)=>{
|
||||||
if(!Number.isFinite(state.opIds[k])){
|
if(!Number.isFinite(state.opIds[k])){
|
||||||
toss("Maintenance required: missing state.opIds[",k,"]");
|
toss("Maintenance required: missing state.opIds[",k,"]");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
initS11n();
|
||||||
metrics.reset();
|
metrics.reset();
|
||||||
log("init state",state);
|
log("init state",state);
|
||||||
wMsg('inited');
|
wMsg('opfs-async-inited');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:{
|
default:{
|
||||||
let err;
|
let err;
|
||||||
const m = vfsAsyncImpls[data.type] || toss("Unknown message type:",data.type);
|
const m = vfsAsyncImpls[data.type] || toss("Unknown message type:",data.type);
|
||||||
try {
|
try {
|
||||||
await m(data.payload).catch((e)=>err=e);
|
await m(...data.args).catch((e)=>err=e);
|
||||||
}catch(e){
|
}catch(e){
|
||||||
err = e;
|
err = e;
|
||||||
}
|
}
|
||||||
@ -425,5 +487,5 @@ navigator.storage.getDirectory().then(function(d){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
wMsg('loaded');
|
wMsg('opfs-async-loaded');
|
||||||
}).catch((e)=>error(e));
|
}).catch((e)=>error(e));
|
||||||
|
14
manifest
14
manifest
@ -1,5 +1,5 @@
|
|||||||
C OPFS\sVFS:\smoved\si/o\sbuffer\sfrom\sper-file\sto\sthe\sVFS,\sand\srelated\srefactoring,\sin\sprep\sfor\sexperimentation\swith\sa\snew\sinter-worker\scomms\smodel.
|
C OPFS\sVFS:\sfurther\sinternal\srefactoring\stowards\sexperimenting\swith\sa\snew\scomms\smodel.
|
||||||
D 2022-09-20T01:28:47.162
|
D 2022-09-20T03:31:02.139
|
||||||
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
||||||
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
||||||
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
|
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
|
||||||
@ -484,7 +484,7 @@ F ext/wasm/api/post-js-header.js 0e853b78db83cb1c06b01663549e0e8b4f377f12f5a2d9a
|
|||||||
F ext/wasm/api/sqlite3-api-cleanup.js 8564a6077cdcaea9a9f428a019af8a05887f0131e6a2a1e72a7ff1145fadfe77
|
F ext/wasm/api/sqlite3-api-cleanup.js 8564a6077cdcaea9a9f428a019af8a05887f0131e6a2a1e72a7ff1145fadfe77
|
||||||
F ext/wasm/api/sqlite3-api-glue.js 366d580c8e5bf7fcf4c6dee6f646c31f5549bd417ea03a59a0acca00e8ecce30
|
F ext/wasm/api/sqlite3-api-glue.js 366d580c8e5bf7fcf4c6dee6f646c31f5549bd417ea03a59a0acca00e8ecce30
|
||||||
F ext/wasm/api/sqlite3-api-oo1.js 2d13dddf0d2b4168a9249f124134d37924331e5b55e05dba18b6d661fbeefe48
|
F ext/wasm/api/sqlite3-api-oo1.js 2d13dddf0d2b4168a9249f124134d37924331e5b55e05dba18b6d661fbeefe48
|
||||||
F ext/wasm/api/sqlite3-api-opfs.js f8027fb4af1c24fcfad31889f8a5ccfa3b96d6e812d3495b13833bef57034046
|
F ext/wasm/api/sqlite3-api-opfs.js 351459d571166ff4cebaccd6b8aad2b0fe5eac54a8c777ba52c31c931a3eb2e2
|
||||||
F ext/wasm/api/sqlite3-api-prologue.js 0d2639387b94c30f492d4aea6e44fb7b16720808678464559458fd2ae3759655
|
F ext/wasm/api/sqlite3-api-prologue.js 0d2639387b94c30f492d4aea6e44fb7b16720808678464559458fd2ae3759655
|
||||||
F ext/wasm/api/sqlite3-api-worker1.js ee4cf149cbacb63d06b536674f822aa5088b7e022cdffc69f1f36cebe2f9fea0
|
F ext/wasm/api/sqlite3-api-worker1.js ee4cf149cbacb63d06b536674f822aa5088b7e022cdffc69f1f36cebe2f9fea0
|
||||||
F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9
|
F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9
|
||||||
@ -519,7 +519,7 @@ F ext/wasm/speedtest1.html 512addeb3c27c94901178b7bcbde83a6f95c093f9ebe16a2959a0
|
|||||||
F ext/wasm/split-speedtest1-script.sh a3e271938d4d14ee49105eb05567c6a69ba4c1f1293583ad5af0cd3a3779e205 x
|
F ext/wasm/split-speedtest1-script.sh a3e271938d4d14ee49105eb05567c6a69ba4c1f1293583ad5af0cd3a3779e205 x
|
||||||
F ext/wasm/sql/000-mandelbrot.sql 775337a4b80938ac8146aedf88808282f04d02d983d82675bd63d9c2d97a15f0
|
F ext/wasm/sql/000-mandelbrot.sql 775337a4b80938ac8146aedf88808282f04d02d983d82675bd63d9c2d97a15f0
|
||||||
F ext/wasm/sql/001-sudoku.sql 35b7cb7239ba5d5f193bc05ec379bcf66891bce6f2a5b3879f2f78d0917299b5
|
F ext/wasm/sql/001-sudoku.sql 35b7cb7239ba5d5f193bc05ec379bcf66891bce6f2a5b3879f2f78d0917299b5
|
||||||
F ext/wasm/sqlite3-opfs-async-proxy.js 7ebc36915cd61bd4f067a4823307f4d4eb2678a173aaae470c534e8fe9cda650
|
F ext/wasm/sqlite3-opfs-async-proxy.js 462081970a6a46d9b2c386474aacad2d81e6629bb554d6cad5c58515f08c8a38
|
||||||
F ext/wasm/sqlite3-worker1-promiser.js 4fd0465688a28a75f1d4ee4406540ba494f49844e3cad0670d0437a001943365
|
F ext/wasm/sqlite3-worker1-promiser.js 4fd0465688a28a75f1d4ee4406540ba494f49844e3cad0670d0437a001943365
|
||||||
F ext/wasm/sqlite3-worker1.js 0c1e7626304543969c3846573e080c082bf43bcaa47e87d416458af84f340a9e
|
F ext/wasm/sqlite3-worker1.js 0c1e7626304543969c3846573e080c082bf43bcaa47e87d416458af84f340a9e
|
||||||
F ext/wasm/test-opfs-vfs.html eb69dda21eb414b8f5e3f7c1cc0f774103cc9c0f87b2d28a33419e778abfbab5
|
F ext/wasm/test-opfs-vfs.html eb69dda21eb414b8f5e3f7c1cc0f774103cc9c0f87b2d28a33419e778abfbab5
|
||||||
@ -2026,8 +2026,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
|
|||||||
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
||||||
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
||||||
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
||||||
P d1f1fe6f1c60640f7770dfb9245c459a09b8d24ec2ddf664dff77c810bd51f96
|
P d4d63e4580ad8d497310608175308c03c517e051d7865cb66aa0b10356612d7d
|
||||||
R e5a1ff53ebe902985ab8e87bba2c71fb
|
R 7fef73d3d6085edd0d87db08b80a36c1
|
||||||
U stephan
|
U stephan
|
||||||
Z d05962442428c5dae722ab00dca47e48
|
Z d9dc6ab88cc2471cfc1f140af8fec063
|
||||||
# Remove this line to create a well-formed Fossil manifest.
|
# Remove this line to create a well-formed Fossil manifest.
|
||||||
|
@ -1 +1 @@
|
|||||||
d4d63e4580ad8d497310608175308c03c517e051d7865cb66aa0b10356612d7d
|
5ca412ced24b4e3af5f467e710a597ed440badf7b8335346aade11d3cad3d1a1
|
Reference in New Issue
Block a user