prex-es5
Version:
Async coordination primitives and extensions on top of ES6 Promises
108 lines (105 loc) • 3.73 kB
JavaScript
/*! *****************************************************************************
Copyright (c) Microsoft Corporation.
Licensed under the Apache License, Version 2.0.
See LICENSE file in the project root for details.
***************************************************************************** */
import { LinkedList } from "./list";
import { CancellationToken, CancelError } from "./cancellation";
import { isMissing, isNumber, isInstance } from "./utils";
const MAX_INT32 = -1 >>> 1;
/**
* Limits the number of asynchronous operations that can access a resource
* or pool of resources.
*/
export class Semaphore {
/**
* Initializes a new instance of the Semaphore class.
*
* @param initialCount The initial number of entries.
* @param maxCount The maximum number of entries.
*/
constructor(initialCount, maxCount) {
this._waiters = new LinkedList();
if (isMissing(maxCount))
maxCount = MAX_INT32;
if (!isNumber(initialCount))
throw new TypeError("Number expected: initialCount.");
if (!isNumber(maxCount))
throw new TypeError("Number expected: maxCount.");
if ((initialCount |= 0) < 0)
throw new RangeError("Argument out of range: initialCount.");
if ((maxCount |= 0) < 1)
throw new RangeError("Argument out of range: maxCount.");
if (initialCount > maxCount)
throw new RangeError("Argument out of range: initialCount.");
this._currentCount = initialCount;
this._maxCount = maxCount;
}
/**
* Gets the number of remaining asynchronous operations that can enter
* the Semaphore.
*/
get count() {
return this._currentCount;
}
/**
* Asynchronously waits for the event to become signaled.
*
* @param token A CancellationToken used to cancel the request.
*/
wait(token) {
return new Promise((resolve, reject) => {
if (isMissing(token))
token = CancellationToken.none;
if (!isInstance(token, CancellationToken))
throw new TypeError("CancellationToken expected: token.");
token.throwIfCancellationRequested();
if (this._currentCount > 0) {
this._currentCount--;
resolve();
return;
}
const node = this._waiters.push(() => {
registration.unregister();
if (token.cancellationRequested) {
reject(new CancelError());
}
else {
resolve();
}
});
const registration = token.register(() => {
if (node.list) {
node.list.deleteNode(node);
reject(new CancelError());
}
});
});
}
/**
* Releases the Semaphore one or more times.
*
* @param count The number of times to release the Semaphore.
*/
release(count) {
if (isMissing(count))
count = 1;
if (!isNumber(count))
throw new TypeError("Number expected: count.");
if ((count |= 0) < 1)
throw new RangeError("Argument out of range: count.");
if (this._maxCount - this._currentCount < count)
throw new RangeError("Argument out of range: count.");
while (count > 0) {
count--;
const resolve = this._waiters.shift();
if (resolve) {
resolve();
}
else {
this._currentCount++;
}
}
}
}
//# sourceMappingURL=semaphore.js.map