import { List } from "./list" ; import { PromiseQueue } from "./promise_queue" ; import { promisifyEvent } from "../utility" ; import * as Events from "events" ; /**
 * Represents a generic resource pool. If no resources are available, will
 * create and register a new one. Uses a doubly-linked list as a FIFO queue for
 * unused resources and a Set for in-use resources.
 */ export class ResourcePool < T > { private readonly create : ( ) => T | PromiseLike < T > ; private readonly destroy : ( resource : T ) => string | PromiseLike < string > ; private readonly emptyEmitter : Events . EventEmitter ; private inUse : Set < T > ; private readonly maxResources : number ; private unused : List < T > ; private resourceCount : number = 0 ; private waiting : PromiseQueue < T > ; param create param destroy returns public constructor ( create , destroy , maxResources ) { this . create = create ; this . destroy = destroy ; this . maxResources = maxResources ; this . emptyEmitter = new Events . EventEmitter ( ) ; this . inUse = new Set < T > ( ) ; this . unused = new List < T > ( ) ; this . waiting = new PromiseQueue < T > ( ) ; } returns public numOwned ( ) { return this . resourceCount ; } returns public numInUse ( ) { return this . resourceCount - this . unused . length ; } returns public numUnused ( ) { return this . unused . length ; } param f returns public async use < U > ( f ) { const resource = await this . get ( ) ; try { const result = f ( resource ) ; return this . returnAfter ( result , resource ) ; } catch ( error ) { return this . returnAfter ( Promise . reject ( error ) , resource ) ; } } returns public async get ( ) { const resource = await this . getResource ( ) ; this . inUse . add ( resource ) ; return resource ; } param promise param resource public async returnAfter < U > ( promise , resource ) { return Promise . resolve ( promise ) . then ( ( value ) => { this . return ( resource ) ; return value ; } ) . catch ( ( reason ) => { this . return ( resource ) ; return Promise . reject ( reason ) ; } ) ; } param resource throws public return ( resource ) { if ( ! this . inUse . delete ( resource ) ) { throw new Error ( "resource does not belong to this pool" ) ; } if ( this . waiting . resolveOne ( resource ) ) { return ; } if ( this . inUse . size === 0 ) { this . emptyEmitter . emit ( "empty" ) ; } this . unused . push ( resource ) ; } returns public async destroyAll ( ) { if ( this . inUse . size !== 0 ) { await promisifyEvent ( this . emptyEmitter , "empty" ) ; } const connections = Array . from ( this . unused ) ; this . unused = new List < T > ( ) ; const replies = connections . map ( ( resource ) => this . destroy ( resource ) ) ; return Promise . all ( replies ) ; } returns private async getResource ( ) { const maybeUnused = this . unused . shift ( ) ; if ( typeof maybeUnused !== "undefined" ) { return maybeUnused ; } else if ( this . resourceCount < this . maxResources ) { ++ this . resourceCount ; return Promise . resolve ( this . create ( ) ) ; } return this . waiting . push ( ) ; } }