aboutsummaryrefslogtreecommitdiff
path: root/ext/wasm/api/sqlite3-opfs-async-proxy.js
diff options
context:
space:
mode:
Diffstat (limited to 'ext/wasm/api/sqlite3-opfs-async-proxy.js')
-rw-r--r--ext/wasm/api/sqlite3-opfs-async-proxy.js74
1 files changed, 52 insertions, 22 deletions
diff --git a/ext/wasm/api/sqlite3-opfs-async-proxy.js b/ext/wasm/api/sqlite3-opfs-async-proxy.js
index 9c0bf4cbe..e4657484e 100644
--- a/ext/wasm/api/sqlite3-opfs-async-proxy.js
+++ b/ext/wasm/api/sqlite3-opfs-async-proxy.js
@@ -44,6 +44,7 @@ if(self.window === self){
this API.
*/
const state = Object.create(null);
+
/**
verbose:
@@ -96,13 +97,27 @@ metrics.dump = ()=>{
};
/**
- Map of sqlite3_file pointers (integers) to metadata related to a
- given OPFS file handles. The pointers are, in this side of the
- interface, opaque file handle IDs provided by the synchronous
- part of this constellation. Each value is an object with a structure
- demonstrated in the xOpen() impl.
+ __openFiles is a map of sqlite3_file pointers (integers) to
+ metadata related to a given OPFS file handles. The pointers are, in
+ this side of the interface, opaque file handle IDs provided by the
+ synchronous part of this constellation. Each value is an object
+ with a structure demonstrated in the xOpen() impl.
*/
const __openFiles = Object.create(null);
+/**
+ __autoLocks is a Set of sqlite3_file pointers (integers) which were
+ "auto-locked". i.e. those for which we obtained a sync access
+ handle without an explicit xLock() call. Such locks will be
+ released during db connection idle time, whereas a sync access
+ handle obtained via xLock(), or subsequently xLock()'d after
+ auto-acquisition, will not be released until xUnlock() is called.
+
+ Maintenance reminder: if we relinquish auto-locks at the end of the
+ operation which acquires them, we pay a massive performance
+ penalty: speedtest1 benchmarks take up to 4x as long. By delaying
+ the lock release until idle time, the hit is negligible.
+*/
+const __autoLocks = new Set();
/**
Expects an OPFS file path. It gets resolved, such that ".."
@@ -191,6 +206,10 @@ const getSyncHandle = async (fh)=>{
}
}
log("Got sync handle for",fh.filenameAbs,'in',performance.now() - t,'ms');
+ if(!fh.xLock){
+ __autoLocks.add(fh.fid);
+ log("Auto-locked",fh.fid,fh.filenameAbs);
+ }
}
return fh.syncHandle;
};
@@ -210,6 +229,8 @@ const closeSyncHandle = async (fh)=>{
log("Closing sync handle for",fh.filenameAbs);
const h = fh.syncHandle;
delete fh.syncHandle;
+ delete fh.xLock;
+ __autoLocks.delete(fh.fid);
return h.close();
}
};
@@ -360,9 +381,10 @@ const vfsAsyncImpls = {
xClose: async function(fid/*sqlite3_file pointer*/){
const opName = 'xClose';
mTimeStart(opName);
+ __autoLocks.delete(fid);
const fh = __openFiles[fid];
let rc = 0;
- wTimeStart('xClose');
+ wTimeStart(opName);
if(fh){
delete __openFiles[fid];
await closeSyncHandle(fh);
@@ -421,7 +443,6 @@ const vfsAsyncImpls = {
mTimeStart('xFileSize');
const fh = __openFiles[fid];
let rc;
- const hadLock = !!fh.syncHandle;
wTimeStart('xFileSize');
try{
affirmLocked('xFileSize',fh);
@@ -432,7 +453,6 @@ const vfsAsyncImpls = {
state.s11n.storeException(2,e);
rc = state.sq3Codes.SQLITE_IOERR;
}
- if(!hadLock) closeSyncHandleNoThrow(fh);
wTimeEnd();
storeAndNotify('xFileSize', rc);
mTimeEnd();
@@ -442,12 +462,17 @@ const vfsAsyncImpls = {
mTimeStart('xLock');
const fh = __openFiles[fid];
let rc = 0;
+ const oldLockType = fh.xLock;
+ fh.xLock = lockType;
if( !fh.syncHandle ){
wTimeStart('xLock');
- try { await getSyncHandle(fh) }
- catch(e){
+ try {
+ await getSyncHandle(fh);
+ __autoLocks.delete(fid);
+ }catch(e){
state.s11n.storeException(1,e);
rc = state.sq3Codes.SQLITE_IOERR_LOCK;
+ fh.xLock = oldLockType;
}
wTimeEnd();
}
@@ -481,6 +506,7 @@ const vfsAsyncImpls = {
*/
wTimeEnd();
__openFiles[fid] = Object.assign(Object.create(null),{
+ fid: fid,
filenameAbs: filename,
filenamePart: filenamePart,
dirHandle: hDir,
@@ -503,7 +529,6 @@ const vfsAsyncImpls = {
mTimeStart('xRead');
let rc = 0, nRead;
const fh = __openFiles[fid];
- const hadLock = !!fh.syncHandle;
try{
affirmLocked('xRead',fh);
wTimeStart('xRead');
@@ -522,7 +547,6 @@ const vfsAsyncImpls = {
state.s11n.storeException(1,e);
rc = state.sq3Codes.SQLITE_IOERR_READ;
}
- if(!hadLock) closeSyncHandleNoThrow(fh);
storeAndNotify('xRead',rc);
mTimeEnd();
},
@@ -547,7 +571,6 @@ const vfsAsyncImpls = {
mTimeStart('xTruncate');
let rc = 0;
const fh = __openFiles[fid];
- const hadLock = !!fh.syncHandle;
wTimeStart('xTruncate');
try{
affirmLocked('xTruncate',fh);
@@ -558,7 +581,6 @@ const vfsAsyncImpls = {
state.s11n.storeException(2,e);
rc = state.sq3Codes.SQLITE_IOERR_TRUNCATE;
}
- if(!hadLock) closeSyncHandleNoThrow(fh);
wTimeEnd();
storeAndNotify('xTruncate',rc);
mTimeEnd();
@@ -585,7 +607,6 @@ const vfsAsyncImpls = {
mTimeStart('xWrite');
let rc;
const fh = __openFiles[fid];
- const hadLock = !!fh.syncHandle;
wTimeStart('xWrite');
try{
affirmLocked('xWrite',fh);
@@ -600,7 +621,6 @@ const vfsAsyncImpls = {
state.s11n.storeException(1,e);
rc = state.sq3Codes.SQLITE_IOERR_WRITE;
}
- if(!hadLock) closeSyncHandleNoThrow(fh);
wTimeEnd();
storeAndNotify('xWrite',rc);
mTimeEnd();
@@ -636,7 +656,7 @@ const initS11n = ()=>{
default: toss("Invalid type ID:",tid);
}
};
- state.s11n.deserialize = function(){
+ state.s11n.deserialize = function(clear=false){
++metrics.s11n.deserialize.count;
const t = performance.now();
const argc = viewU8[0];
@@ -661,6 +681,7 @@ const initS11n = ()=>{
rc.push(v);
}
}
+ if(clear) viewU8[0] = 0;
//log("deserialize:",argc, rc);
metrics.s11n.deserialize.time += performance.now() - t;
return rc;
@@ -727,21 +748,30 @@ const waitLoop = async function f(){
We need to wake up periodically to give the thread a chance
to do other things.
*/
- const waitTime = 1000;
+ const waitTime = 500;
while(!flagAsyncShutdown){
try {
if('timed-out'===Atomics.wait(
state.sabOPView, state.opIds.whichOp, 0, waitTime
)){
+ if(__autoLocks.size){
+ /* Release all auto-locks. */
+ for(const fid of __autoLocks){
+ const fh = __openFiles[fid];
+ await closeSyncHandleNoThrow(fh);
+ log("Auto-unlocked",fid,fh.filenameAbs);
+ }
+ }
continue;
}
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() || [];
- state.s11n.serialize(/* clear s11n to keep the caller from
- confusing this with an exception string
- written by the upcoming operation */);
+ const args = state.s11n.deserialize(
+ true /* clear s11n to keep the caller from confusing this with
+ an exception string written by the upcoming
+ operation */
+ ) || [];
//warn("waitLoop() whichOp =",opId, hnd, args);
if(hnd.f) await hnd.f(...args);
else error("Missing callback for opId",opId);