/
WebLocks.js
50 lines (43 loc) · 1.38 KB
/
WebLocks.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
import * as VFS from '../VFS.js';
const WEB_LOCKS = navigator['locks'] ?? console.warn('concurrency is unsafe without Web Locks API');
export class WebLocks {
/** @type {Map<number, number>} */ #mapIdToState = new Map();
/** @type {Map<string, (any) => void>} */ #mapNameToReleaser = new Map();
// Use a single exclusive lock.
async lock(name, flags) {
const lockState = this.#mapIdToState.get(name) ?? VFS.SQLITE_LOCK_NONE;
if (lockState === VFS.SQLITE_LOCK_NONE) {
await this.#acquireWebLock(name, 'exclusive');
}
this.#mapIdToState.set(name, flags);
return VFS.SQLITE_OK
}
async unlock(name, flags) {
if (flags === VFS.SQLITE_LOCK_NONE) {
this.#releaseWebLock(name);
this.#mapIdToState.delete(name);
} else {
this.#mapIdToState.set(name, flags);
}
return VFS.SQLITE_OK
}
async #acquireWebLock(name, mode, signal) {
if (WEB_LOCKS) {
const lockName = `sqlite-${name}`;
return new Promise(async (hasLock, aborted) => {
try {
await WEB_LOCKS.request(lockName, { mode, signal }, () => new Promise(release => {
hasLock();
this.#mapNameToReleaser.set(name, release);
}));
} catch(e) {
aborted(e);
}
});
}
}
#releaseWebLock(name) {
this.#mapNameToReleaser.get(name)?.();
this.#mapNameToReleaser.delete(name);
}
};