site/node_modules/async-mutex/index.mjs
2024-10-14 08:09:33 +02:00

292 lines
11 KiB
JavaScript

const E_TIMEOUT = new Error('timeout while waiting for mutex to become available');
const E_ALREADY_LOCKED = new Error('mutex already locked');
const E_CANCELED = new Error('request for lock canceled');
var __awaiter$2 = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
class Semaphore {
constructor(_value, _cancelError = E_CANCELED) {
this._value = _value;
this._cancelError = _cancelError;
this._queue = [];
this._weightedWaiters = [];
}
acquire(weight = 1, priority = 0) {
if (weight <= 0)
throw new Error(`invalid weight ${weight}: must be positive`);
return new Promise((resolve, reject) => {
const task = { resolve, reject, weight, priority };
const i = findIndexFromEnd(this._queue, (other) => priority <= other.priority);
if (i === -1 && weight <= this._value) {
// Needs immediate dispatch, skip the queue
this._dispatchItem(task);
}
else {
this._queue.splice(i + 1, 0, task);
}
});
}
runExclusive(callback_1) {
return __awaiter$2(this, arguments, void 0, function* (callback, weight = 1, priority = 0) {
const [value, release] = yield this.acquire(weight, priority);
try {
return yield callback(value);
}
finally {
release();
}
});
}
waitForUnlock(weight = 1, priority = 0) {
if (weight <= 0)
throw new Error(`invalid weight ${weight}: must be positive`);
if (this._couldLockImmediately(weight, priority)) {
return Promise.resolve();
}
else {
return new Promise((resolve) => {
if (!this._weightedWaiters[weight - 1])
this._weightedWaiters[weight - 1] = [];
insertSorted(this._weightedWaiters[weight - 1], { resolve, priority });
});
}
}
isLocked() {
return this._value <= 0;
}
getValue() {
return this._value;
}
setValue(value) {
this._value = value;
this._dispatchQueue();
}
release(weight = 1) {
if (weight <= 0)
throw new Error(`invalid weight ${weight}: must be positive`);
this._value += weight;
this._dispatchQueue();
}
cancel() {
this._queue.forEach((entry) => entry.reject(this._cancelError));
this._queue = [];
}
_dispatchQueue() {
this._drainUnlockWaiters();
while (this._queue.length > 0 && this._queue[0].weight <= this._value) {
this._dispatchItem(this._queue.shift());
this._drainUnlockWaiters();
}
}
_dispatchItem(item) {
const previousValue = this._value;
this._value -= item.weight;
item.resolve([previousValue, this._newReleaser(item.weight)]);
}
_newReleaser(weight) {
let called = false;
return () => {
if (called)
return;
called = true;
this.release(weight);
};
}
_drainUnlockWaiters() {
if (this._queue.length === 0) {
for (let weight = this._value; weight > 0; weight--) {
const waiters = this._weightedWaiters[weight - 1];
if (!waiters)
continue;
waiters.forEach((waiter) => waiter.resolve());
this._weightedWaiters[weight - 1] = [];
}
}
else {
const queuedPriority = this._queue[0].priority;
for (let weight = this._value; weight > 0; weight--) {
const waiters = this._weightedWaiters[weight - 1];
if (!waiters)
continue;
const i = waiters.findIndex((waiter) => waiter.priority <= queuedPriority);
(i === -1 ? waiters : waiters.splice(0, i))
.forEach((waiter => waiter.resolve()));
}
}
}
_couldLockImmediately(weight, priority) {
return (this._queue.length === 0 || this._queue[0].priority < priority) &&
weight <= this._value;
}
}
function insertSorted(a, v) {
const i = findIndexFromEnd(a, (other) => v.priority <= other.priority);
a.splice(i + 1, 0, v);
}
function findIndexFromEnd(a, predicate) {
for (let i = a.length - 1; i >= 0; i--) {
if (predicate(a[i])) {
return i;
}
}
return -1;
}
var __awaiter$1 = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
class Mutex {
constructor(cancelError) {
this._semaphore = new Semaphore(1, cancelError);
}
acquire() {
return __awaiter$1(this, arguments, void 0, function* (priority = 0) {
const [, releaser] = yield this._semaphore.acquire(1, priority);
return releaser;
});
}
runExclusive(callback, priority = 0) {
return this._semaphore.runExclusive(() => callback(), 1, priority);
}
isLocked() {
return this._semaphore.isLocked();
}
waitForUnlock(priority = 0) {
return this._semaphore.waitForUnlock(1, priority);
}
release() {
if (this._semaphore.isLocked())
this._semaphore.release();
}
cancel() {
return this._semaphore.cancel();
}
}
var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
function withTimeout(sync, timeout, timeoutError = E_TIMEOUT) {
return {
acquire: (weightOrPriority, priority) => {
let weight;
if (isSemaphore(sync)) {
weight = weightOrPriority;
}
else {
weight = undefined;
priority = weightOrPriority;
}
if (weight !== undefined && weight <= 0) {
throw new Error(`invalid weight ${weight}: must be positive`);
}
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
let isTimeout = false;
const handle = setTimeout(() => {
isTimeout = true;
reject(timeoutError);
}, timeout);
try {
const ticket = yield (isSemaphore(sync)
? sync.acquire(weight, priority)
: sync.acquire(priority));
if (isTimeout) {
const release = Array.isArray(ticket) ? ticket[1] : ticket;
release();
}
else {
clearTimeout(handle);
resolve(ticket);
}
}
catch (e) {
if (!isTimeout) {
clearTimeout(handle);
reject(e);
}
}
}));
},
runExclusive(callback, weight, priority) {
return __awaiter(this, void 0, void 0, function* () {
let release = () => undefined;
try {
const ticket = yield this.acquire(weight, priority);
if (Array.isArray(ticket)) {
release = ticket[1];
return yield callback(ticket[0]);
}
else {
release = ticket;
return yield callback();
}
}
finally {
release();
}
});
},
release(weight) {
sync.release(weight);
},
cancel() {
return sync.cancel();
},
waitForUnlock: (weightOrPriority, priority) => {
let weight;
if (isSemaphore(sync)) {
weight = weightOrPriority;
}
else {
weight = undefined;
priority = weightOrPriority;
}
if (weight !== undefined && weight <= 0) {
throw new Error(`invalid weight ${weight}: must be positive`);
}
return new Promise((resolve, reject) => {
const handle = setTimeout(() => reject(timeoutError), timeout);
(isSemaphore(sync)
? sync.waitForUnlock(weight, priority)
: sync.waitForUnlock(priority)).then(() => {
clearTimeout(handle);
resolve();
});
});
},
isLocked: () => sync.isLocked(),
getValue: () => sync.getValue(),
setValue: (value) => sync.setValue(value),
};
}
function isSemaphore(sync) {
return sync.getValue !== undefined;
}
// eslint-disable-next-lisne @typescript-eslint/explicit-module-boundary-types
function tryAcquire(sync, alreadyAcquiredError = E_ALREADY_LOCKED) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return withTimeout(sync, 0, alreadyAcquiredError);
}
export { E_ALREADY_LOCKED, E_CANCELED, E_TIMEOUT, Mutex, Semaphore, tryAcquire, withTimeout };