}
log("Got",opName+"() sync handle for",fh.filenameAbs,
'in',performance.now() - t,'ms');
- if(!fh.xLock){
+ if(!fh.xLock && !state.lock/*set by opfs-wl*/){
__implicitLocks.add(fh.fid);
log("Acquired implicit lock for",opName+"()",fh.fid,fh.filenameAbs);
}
return state.s11n;
}/*initS11n()*/;
+ /**
+ Starts a new WebLock request.
+ */
+ const handleLockRequest = async function(){
+ const args = state.s11n.deserialize(true)
+ || toss("Expecting a filename argument from the proxy xLock()");
+ const view = state.sabOPView;
+ const slock = state.lock;
+ const lockType = Atomics.load(view, slock.type);
+
+ await navigator.locks.request('sqlite3-vfs-opfs:'+args[0], {
+ mode: (lockType===state.sq3Codes.SQLITE_LOCK_EXCLUSIVE)
+ ? 'exclusive' : 'shared'
+ }, async (wl)=>{
+ /**
+ A. Tell the C-side we have the browser lock. We use the same
+ handshake slot, but a specific 'Granted' value.
+ */
+ Atomics.store(view, slock.atomicsHandshake, 2);
+ Atomics.notify(view, slock.atomicsHandshake);
+ /**
+ B. Sit here and keep the lock room occupied until the
+ Receptionist receives 'unlockControl'.
+ */
+ while( 1!==Atomics.load(view, slock.atomicsHandshake) ){
+ Atomics.wait(view. slock.atomicsHandshake, 2);
+ }
+ /* C. Reset the handshake slot. */
+ Atomics.store(view, slock.atomicsHandshake, 0);
+ Atomics.notify(view, lock.atomicsHandshake);
+ });
+ };
+
const waitLoop = async function f(){
const opHandlers = Object.create(null);
for(let k of Object.keys(state.opIds)){
o.key = k;
o.f = vi;
}
+ const opIds = state.opIds;
while(!flagAsyncShutdown){
try {
if('not-equal'!==Atomics.wait(
- state.sabOPView, state.opIds.whichOp, 0, state.asyncIdleWaitTime
+ state.sabOPView, opIds.whichOp, 0, state.asyncIdleWaitTime
)){
/* Maintenance note: we compare against 'not-equal' because
await releaseImplicitLocks();
continue;
}
- const opId = Atomics.load(state.sabOPView, state.opIds.whichOp);
- Atomics.store(state.sabOPView, state.opIds.whichOp, 0);
+ const opId = Atomics.exchange(state.sabOPView, opIds.whichOp, 0);
+ if( opId===opIds.lockControl ){
+ handleLockRequest();
+ continue;
+ }
const hnd = opHandlers[opId] ?? toss("No waitLoop handler for whichOp #",opId);
const args = state.s11n.deserialize(
true /* clear s11n to keep the caller from confusing this with
state.opIds.xWrite = i++;
state.opIds.mkdir = i++;
state.opIds.lockControl = i++ /* we signal the intent to lock here */;
- state.opIds.lockType
/** Internal signals which are used only during development and
testing via the dev console. */
state.opIds['opfs-async-metrics'] = i++;
/* Slots for submitting the lock type and receiving its acknowledgement. */
state.lock = nu({
- type: i++,
- atomicsHandshake: i++
+ type: i++ /* SQLITE_LOCK_xyz value */,
+ atomicsHandshake: i++ /* 1=release, 2=granted */
});
state.sabOP = new SharedArrayBuffer(
i * 4/* ==sizeof int32, noting that Atomics.wait() and friends
'SQLITE_OPEN_CREATE',
'SQLITE_OPEN_DELETEONCLOSE',
'SQLITE_OPEN_MAIN_DB',
- 'SQLITE_OPEN_READONLY'
+ 'SQLITE_OPEN_READONLY',
+ 'SQLITE_LOCK_NONE',
+ 'SQLITE_LOCK_SHARED',
+ 'SQLITE_LOCK_RESERVED',
+ 'SQLITE_LOCK_PENDING',
+ 'SQLITE_LOCK_EXCLUSIVE'
].forEach((k)=>{
if(undefined === (state.sq3Codes[k] = capi[k])){
toss("Maintenance required: not found:",k);
mTimeEnd();
return rc;
},
- xLock: function(pFile,lockType){
+ xLock: function(pFile, lockType){
mTimeStart('xLock');
const f = __openFiles[pFile];
let rc = 0;
type. If no lock is active, have the async counterpart
lock the file. */
if( !f.lockType ) {
- rc = opRun('xLock', pFile, lockType);
- if( 0===rc ) f.lockType = lockType;
+ try{
+ const view = state.sabOPView;
+ /* We need to pass pFile's name through so that the other
+ side can create the WebLock name. */
+ state.s11n.serialize(f.filename)
+ Atomics.store(view, state.lock.type, lockType);
+ Atomics.store(view, state.opIds.whichOp, state.opIds.lockControl);
+ Atomics.notify(state.sabOPView, state.opIds.whichOp)
+ while('not-equal'!==Atomics.wait(view, state.lock.atomicsHandshake, 0)){
+ /* Loop is a workaround for environment-specific quirks. See
+ notes in similar loops. */
+ }
+ f.lockType = lockType;
+ }catch(e){
+ error("xLock(",arguments,") failed", e, f);
+ rc = capi.SQLITE_IOERR_LOCK;
+ }
}else{
f.lockType = lockType;
}
mTimeStart('xUnlock');
const f = __openFiles[pFile];
let rc = 0;
- if( capi.SQLITE_LOCK_NONE === lockType
- && f.lockType ){
- rc = opRun('xUnlock', pFile, lockType);
+ if( lockType < f.lockType ){
+ try{
+ const view = state.sabOPView;
+ Atomics.store(view, state.lock.atomicsHandshake, 1);
+ Atomics.notify(view, state.lock.atomicsHandshake);
+ Atomics.wait(view, state.lock.atomicsHandshake, 1);
+ }catch(e){
+ error("xUnlock(",pFile,lockType,") failed",e, f);
+ rc = capi.SQLITE_IOERR_LOCK;
+ }
}
if( 0===rc ) f.lockType = lockType;
mTimeEnd();
}
const fh = Object.create(null);
fh.fid = pFile;
- fh.filename = zName;
+ fh.filename = wasm.cstrToJs(zName);
fh.sab = new SharedArrayBuffer(state.fileBufferSize);
fh.flags = flags;
fh.readOnly = !(capi.SQLITE_OPEN_CREATE & flags)
-C Merge\strunk\sinto\sthe\sopfs-wl\sbranch.
-D 2026-03-03T18:34:59.705
+C Implementations\sof\sWebLock-based\sopfs::xLock()\sand\sxUnlock().\sStill\scompletely\suntested\sand\snot\syet\sintegrated\sinto\sthe\sbuild.
+D 2026-03-03T21:07:46.412
F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F ext/wasm/api/sqlite3-api-prologue.js 1fefd40ab21e3dbf46f43b6fafb07f13eb13cc157a884f7c1134caf631ddb3f2
F ext/wasm/api/sqlite3-api-worker1.c-pp.js 1041dd645e8e821c082b628cd8d9acf70c667430f9d45167569633ffc7567938
F ext/wasm/api/sqlite3-license-version-header.js 98d90255a12d02214db634e041c8e7f2f133d9361a8ebf000ba9c9af4c6761cc
-F ext/wasm/api/sqlite3-opfs-async-proxy.js 92d6d327a862f1627ff3e88e60fdfea9def06ad539d98929ba46490e64372736
+F ext/wasm/api/sqlite3-opfs-async-proxy.js 912ddb17627e933eb9596d393227f7ea47b1136fc2fb0d957d9979e71de59e81
F ext/wasm/api/sqlite3-vfs-helper.c-pp.js 3f828cc66758acb40e9c5b4dcfd87fd478a14c8fb7f0630264e6c7fa0e57515d
F ext/wasm/api/sqlite3-vfs-kvvfs.c-pp.js 2ccf4322f42063aefc150972943e750c77f7926b866f1639d40eec05df075b6e
F ext/wasm/api/sqlite3-vfs-opfs-sahpool.c-pp.js 1575ea6bbcf2da1e6df6892c17521a0c1c1c199a672e9090176ea0b88de48bd9
-F ext/wasm/api/sqlite3-vfs-opfs-wl.c-pp.js 38484644c21b21b97f60979dddd620e47bdba628bde4ae62b6ce8859870e4f85
+F ext/wasm/api/sqlite3-vfs-opfs-wl.c-pp.js c3e453a5736ee1bb9b08247c6e97c1433933a320c7548bedd20481eae7922a48
F ext/wasm/api/sqlite3-vfs-opfs.c-pp.js 88ce2078267a2d1af57525a32d896295f4a8db7664de0e17e82dc9ff006ed8d3
F ext/wasm/api/sqlite3-vtab-helper.c-pp.js 366596d8ff73d4cefb938bbe95bc839d503c3fab6c8335ce4bf52f0d8a7dee81
F ext/wasm/api/sqlite3-wasm.c 45bb20e19b245136711f9b78584371233975811b6560c29ed9b650e225417e29
F tool/warnings.sh d924598cf2f55a4ecbc2aeb055c10bd5f48114793e7ba25f9585435da29e7e98
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
F tool/winmain.c 00c8fb88e365c9017db14c73d3c78af62194d9644feaf60e220ab0f411f3604c
-P 372f18ae909bafd7167b70051935832e3107ede72aaeccbf5c042db7ba791912 88dce64242552e7443d9fb496f6f3ad71dc5b4a882ce21b7ef1d5ea4e26f1e61
-R 75fb07a85007c6410c69c66cd4391b75
+P 81bc4b3b6abc19f98d1f3b1065c4b39a42620a0d7abebe98605dca62dd2d6f9a
+R f9d18b4e8da91823211b005773e17736
U stephan
-Z 42a2789071a18d2bcb63e8ba8c976a45
+Z e389fe2e9ad12fa6369004db9e3f215f
# Remove this line to create a well-formed Fossil manifest.
-81bc4b3b6abc19f98d1f3b1065c4b39a42620a0d7abebe98605dca62dd2d6f9a
+3343e3aabe465f4ab91dd148dfc5a60346e9afb560c10d1f92aeae98763ec3ce