/** Semaphores are initialized with a number of permits that get aquired and released
 * over the lifecycle of the semaphore. These permits limit the number of simultaneous
 * executions of the code that the semaphore synchronizes. Functions can wait on a semaphore
 * and stop executing until a permit becomes available.
 *
 * Locks are a special case of semaphores that only allow one execution of a critical section.
 * If you need a lock, you can import the Lock class from this package.
 *
 * This Semaphore class is implemented with the help of promises that get returned
 * by functions that wait for permits to become available. This makes it possible
 * to use async/await to synchronize your code.
 */ export default class Semaphore { private permits : number ; private promiseResolverQueue : Array < ( v : boolean ) => void > = [ ] ; param permits constructor ( permits ) { this . permits = permits ; } returns public getPermits ( ) { return this . permits ; } returns public async wait ( ) { if ( this . permits > 0 ) { this . permits -= 1 ; return Promise . resolve ( true ) ; } return new Promise < boolean > ( resolver => this . promiseResolverQueue . push ( resolver ) ) ; } returns public async acquire ( ) { return this . wait ( ) ; } param milliseconds returns public async waitFor ( milliseconds ) { if ( this . permits > 0 ) { this . permits -= 1 ; return Promise . resolve ( true ) ; } let resolver = b => void 0 ; const promise = new Promise < boolean > ( r => { resolver = r ; } ) ; this . promiseResolverQueue . push ( resolver ) ; setTimeout ( ( ) => { const index = this . promiseResolverQueue . indexOf ( resolver ) ; if ( index !== - 1 ) { this . promiseResolverQueue . splice ( index , 1 ) ; } else { console . warn ( `Semaphore.waitFor couldn't find its promise resolver in the queue` ) ; } resolver ( false ) ; } , milliseconds ) ; return promise ; } returns public tryAcquire ( ) { if ( this . permits > 0 ) { this . permits -= 1 ; return true ; } return false ; } returns public drainPermits ( ) { if ( this . permits > 0 ) { const permitCount = this . permits ; this . permits = 0 ; return permitCount ; } return 0 ; } /**
     * Increases the number of permits by one. If there are other functions waiting, one of them will
     * continue to execute in a future iteration of the event loop.
     */ public signal ( ) { this . permits += 1 ; if ( this . permits > 1 && this . promiseResolverQueue . length > 0 ) { console . warn ( 'Semaphore.permits should never be > 0 when there is someone waiting.' ) ; } else if ( this . permits === 1 && this . promiseResolverQueue . length > 0 ) { this . permits -= 1 ; const nextResolver = this . promiseResolverQueue . shift ( ) ; if ( nextResolver ) { nextResolver ( true ) ; } } } /**
     * Alias for {@linkcode Semaphore.signal}.
     */ public release ( ) { this . signal ( ) ; } typeparam param func return public async execute < T > ( func ) { await this . wait ( ) ; try { return await func ( ) ; } finally { this . signal ( ) ; } } }