"use strict";
//
//  Copyright (c) Microsoft Corporation. All rights reserved.
//
Object.defineProperty(exports, "__esModule", { value: true });
exports.Semaphore = void 0;
const promiseCompletionSource_1 = require("./promiseCompletionSource");
const cancellation_1 = require("./cancellation");
const errors_1 = require("../errors");
/**
 * Semaphore-like object that allows multiple awaiters to coordinate exclusive access to a resource.
 */
class Semaphore {
    /**
     * Creates a new semaphore instance.
     * @param initialCount Optional initial count. Defaults to 0.
     */
    constructor(initialCount = 0) {
        this.completions = [];
        this.disposed = false;
        this.count = initialCount;
    }
    /**
     * Gets the current available count of the semaphore.
     */
    get currentCount() {
        return this.count;
    }
    /**
     * Releases the semaphore, increasing the available count or unblicking one or more awaiters.
     * @param releaseCount Optional specified count to release. Defaults to 1.
     * @returns The previous count (before release).
     */
    release(releaseCount = 1) {
        if (this.disposed)
            throw new errors_1.ObjectDisposedError(this);
        const previousCount = this.count;
        for (; releaseCount > 0; releaseCount--) {
            if (this.completions.length > 0) {
                // Something is waiting on the semaphore.
                // Remove and complete the wait without incrementing the count.
                const completion = this.completions.shift();
                completion.resolve(true);
            }
            else {
                // Nothing is currently waiting on the semaphore. Increment the available count.
                this.count++;
            }
        }
        return previousCount;
    }
    /**
     * Releases the semaphore, but does not throw an `ObjectDisposedError` if it is already disposed.
     */
    tryRelease() {
        try {
            this.release();
        }
        catch (e) {
            if (!(e instanceof errors_1.ObjectDisposedError)) {
                throw e;
            }
        }
    }
    async wait(timeoutOrCancellation, cancellation) {
        const millisecondsTimeout = typeof timeoutOrCancellation === 'number' ? timeoutOrCancellation : undefined;
        if (typeof cancellation === 'undefined' && typeof timeoutOrCancellation === 'object') {
            cancellation = timeoutOrCancellation;
        }
        if (this.disposed)
            throw new errors_1.ObjectDisposedError(this);
        if (cancellation === null || cancellation === void 0 ? void 0 : cancellation.isCancellationRequested)
            throw new cancellation_1.CancellationError();
        if (this.count > 0) {
            // The semaphore is available now.
            this.count--;
            return true;
        }
        else if (millisecondsTimeout === 0) {
            // The semaphore is not available and the caller doesn't want to wait.
            return false;
        }
        else {
            const completion = new promiseCompletionSource_1.PromiseCompletionSource();
            this.completions.push(completion);
            // Start with a promise that completes with `true` when the wait succeeds.
            const promises = [completion.promise];
            if (millisecondsTimeout) {
                // Race against a promise that completes with `false` when the timeout expires.
                promises.push(new Promise((resolve) => setTimeout(() => resolve(false), millisecondsTimeout)));
            }
            if (cancellation) {
                // Race against a promise that throws when the cancellation token is cancelled.
                const cancellationCompletion = new promiseCompletionSource_1.PromiseCompletionSource();
                cancellation.onCancellationRequested(() => {
                    cancellationCompletion.reject(new cancellation_1.CancellationError());
                });
                promises.push(cancellationCompletion.promise);
            }
            if (await Promise.race(promises)) {
                // The wait succeeded.
                return true;
            }
            else {
                // The wait timed out. Remove the (not-completed) completion from the array.
                const completionIndex = this.completions.indexOf(completion);
                if (completionIndex >= 0)
                    this.completions.splice(completionIndex, 1);
                return false;
            }
        }
    }
    /**
     * Disposes the semaphore and throws a diposed error to any awaiters.
     */
    dispose() {
        if (this.disposed)
            return;
        this.disposed = true;
        for (const completion of this.completions) {
            completion.reject(new errors_1.ObjectDisposedError(this));
        }
        this.completions.splice(0, this.completions.length);
        this.count = 0;
    }
}
exports.Semaphore = Semaphore;
//# sourceMappingURL=https://github.com/VSCodium/sourcemaps/releases/download/stable-4c0b0c6cc561d2d3636d1ec250935431876ce4dc/node_modules-@microsoft-dev-tunnels-ssh-util-semaphore.js.map