You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
80 lines
1.8 KiB
JavaScript
80 lines
1.8 KiB
JavaScript
function makeWaiter() {
|
|
let resolve, reject;
|
|
const wait = new Promise((res, rej) => {
|
|
resolve = res;
|
|
reject = rej;
|
|
});
|
|
return {wait, resolve, reject};
|
|
}
|
|
|
|
class AsyncLock {
|
|
#locked = false;
|
|
#queue = [];
|
|
|
|
get locked() {
|
|
return this.#locked;
|
|
}
|
|
|
|
async runExclusive(callback, timeout = -1) {
|
|
const release = await this.acquire(timeout);
|
|
|
|
try {
|
|
return await callback();
|
|
} finally {
|
|
release();
|
|
}
|
|
}
|
|
|
|
async acquire(timeout = -1) {
|
|
if (!this.#locked) {
|
|
this.#locked = true;
|
|
return this.#newReleaser();
|
|
}
|
|
|
|
if (timeout === 0)
|
|
throw new TimeoutError();
|
|
|
|
let {wait, resolve, reject} = makeWaiter();
|
|
if (timeout > 0) {
|
|
const timer = new AsyncTimer(timeout, () => reject(new TimeoutError()));
|
|
timer.start();
|
|
this.#queue.push({ resolve, reject, timer });
|
|
} else {
|
|
this.#queue.push({ resolve, reject, timer: undefined });
|
|
}
|
|
this.#dispatch();
|
|
return await wait;
|
|
}
|
|
|
|
#dispatch() {
|
|
if (!this.#locked && this.#queue.length) {
|
|
const entry = this.#queue.shift();
|
|
this.#locked = true;
|
|
if (entry.timer) clearTimeout(entry.timer);
|
|
entry.resolve(this.#newReleaser());
|
|
}
|
|
}
|
|
|
|
#newReleaser() {
|
|
let called = false;
|
|
|
|
return () => {
|
|
if (called) return;
|
|
called = true;
|
|
|
|
this.#release();
|
|
};
|
|
}
|
|
|
|
#release() {
|
|
if (this.#locked)
|
|
this.#locked = false;
|
|
this.#dispatch();
|
|
}
|
|
|
|
cancel() {
|
|
this.#queue.forEach((entry) => entry.reject(this._cancelError));
|
|
this.#queue = [];
|
|
}
|
|
}
|