/*! *****************************************************************************
Copyright (c) Microsoft Corporation.
Licensed under the Apache License, Version 2.0.

See LICENSE file in the project root for details.
***************************************************************************** */
"use strict";
const list_1 = require("./list");
const cancellation_1 = require("./cancellation");
const utils_1 = require("./utils");
const MAX_INT32 = -1 >>> 1;
/**
 * Limits the number of asynchronous operations that can access a resource
 * or pool of resources.
 */
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 list_1.LinkedList();
        if (utils_1.isMissing(maxCount))
            maxCount = MAX_INT32;
        if (!utils_1.isNumber(initialCount))
            throw new TypeError("Number expected: initialCount.");
        if (!utils_1.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 (utils_1.isMissing(token))
                token = cancellation_1.CancellationToken.none;
            if (!utils_1.isInstance(token, cancellation_1.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 cancellation_1.CancelError());
                }
                else {
                    resolve();
                }
            });
            const registration = token.register(() => {
                if (node.list) {
                    node.list.deleteNode(node);
                    reject(new cancellation_1.CancelError());
                }
            });
        });
    }
    /**
     * Releases the Semaphore one or more times.
     *
     * @param count The number of times to release the Semaphore.
     */
    release(count) {
        if (utils_1.isMissing(count))
            count = 1;
        if (!utils_1.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++;
            }
        }
    }
}
exports.Semaphore = Semaphore;

//# sourceMappingURL=semaphore.js.map
