async-mutex
Version:
A mutex for guarding async workflows
116 lines (115 loc) • 4.58 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var errors_1 = require("./errors");
var Semaphore = /** @class */ (function () {
function Semaphore(_value, _cancelError) {
if (_cancelError === void 0) { _cancelError = errors_1.E_CANCELED; }
this._value = _value;
this._cancelError = _cancelError;
this._weightedQueues = [];
this._weightedWaiters = [];
}
Semaphore.prototype.acquire = function (weight) {
var _this = this;
if (weight === void 0) { weight = 1; }
if (weight <= 0)
throw new Error("invalid weight ".concat(weight, ": must be positive"));
return new Promise(function (resolve, reject) {
if (!_this._weightedQueues[weight - 1])
_this._weightedQueues[weight - 1] = [];
_this._weightedQueues[weight - 1].push({ resolve: resolve, reject: reject });
_this._dispatch();
});
};
Semaphore.prototype.runExclusive = function (callback, weight) {
if (weight === void 0) { weight = 1; }
return tslib_1.__awaiter(this, void 0, void 0, function () {
var _a, value, release;
return tslib_1.__generator(this, function (_b) {
switch (_b.label) {
case 0: return [4 /*yield*/, this.acquire(weight)];
case 1:
_a = _b.sent(), value = _a[0], release = _a[1];
_b.label = 2;
case 2:
_b.trys.push([2, , 4, 5]);
return [4 /*yield*/, callback(value)];
case 3: return [2 /*return*/, _b.sent()];
case 4:
release();
return [7 /*endfinally*/];
case 5: return [2 /*return*/];
}
});
});
};
Semaphore.prototype.waitForUnlock = function (weight) {
var _this = this;
if (weight === void 0) { weight = 1; }
if (weight <= 0)
throw new Error("invalid weight ".concat(weight, ": must be positive"));
return new Promise(function (resolve) {
if (!_this._weightedWaiters[weight - 1])
_this._weightedWaiters[weight - 1] = [];
_this._weightedWaiters[weight - 1].push(resolve);
_this._dispatch();
});
};
Semaphore.prototype.isLocked = function () {
return this._value <= 0;
};
Semaphore.prototype.getValue = function () {
return this._value;
};
Semaphore.prototype.setValue = function (value) {
this._value = value;
this._dispatch();
};
Semaphore.prototype.release = function (weight) {
if (weight === void 0) { weight = 1; }
if (weight <= 0)
throw new Error("invalid weight ".concat(weight, ": must be positive"));
this._value += weight;
this._dispatch();
};
Semaphore.prototype.cancel = function () {
var _this = this;
this._weightedQueues.forEach(function (queue) { return queue.forEach(function (entry) { return entry.reject(_this._cancelError); }); });
this._weightedQueues = [];
};
Semaphore.prototype._dispatch = function () {
var _a;
for (var weight = this._value; weight > 0; weight--) {
var queueEntry = (_a = this._weightedQueues[weight - 1]) === null || _a === void 0 ? void 0 : _a.shift();
if (!queueEntry)
continue;
var previousValue = this._value;
var previousWeight = weight;
this._value -= weight;
weight = this._value + 1;
queueEntry.resolve([previousValue, this._newReleaser(previousWeight)]);
}
this._drainUnlockWaiters();
};
Semaphore.prototype._newReleaser = function (weight) {
var _this = this;
var called = false;
return function () {
if (called)
return;
called = true;
_this.release(weight);
};
};
Semaphore.prototype._drainUnlockWaiters = function () {
for (var weight = this._value; weight > 0; weight--) {
if (!this._weightedWaiters[weight - 1])
continue;
this._weightedWaiters[weight - 1].forEach(function (waiter) { return waiter(); });
this._weightedWaiters[weight - 1] = [];
}
};
return Semaphore;
}());
exports.default = Semaphore;