import { isMissing , isNumber , isFunction } from "@esfx/internal-guards" ; import { Tag } from "@esfx/internal-tag" ; import { Cancelable } from "@esfx/cancelable" ; import { WaitQueue } from "@esfx/async-waitqueue" ; /**
 * Enables multiple tasks to cooperatively work on an algorithm through
 * multiple phases.
 */ @ Tag ( ) export class AsyncBarrier { private _isExecutingPostPhaseAction = false ; private _postPhaseAction : ( ( barrier : AsyncBarrier ) => void | PromiseLike < void > ) | undefined ; private _phaseNumber : number = 0 ; private _participantCount : number ; private _remainingParticipants : number ; private _waiters = new WaitQueue < void > ( ) ; param participantCount param postPhaseAction constructor ( participantCount , postPhaseAction ? ) { if ( ! isNumber ( participantCount ) ) throw new TypeError ( "Number expected: participantCount." ) ; if ( ( participantCount |= 0 ) < 0 ) throw new RangeError ( "Argument out of range: participantCount." ) ; if ( ! isMissing ( postPhaseAction ) && ! isFunction ( postPhaseAction ) ) throw new TypeError ( "Function expected: postPhaseAction." ) ; this . _participantCount = participantCount ; this . _remainingParticipants = participantCount ; this . _postPhaseAction = postPhaseAction ; } /**
     * Gets the number of the Barrier's current phase.
     */ get currentPhaseNumber ( ) { return this . _phaseNumber ; } /**
     * Gets the total number of participants in the barrier.
     */ get participantCount ( ) { return this . _participantCount ; } /**
     * Gets the number of participants in the barrier that haven't yet signaled in the current phase.
     */ get remainingParticipants ( ) { return this . _remainingParticipants ; } param participantCount add ( participantCount ? ) { if ( isMissing ( participantCount ) ) participantCount = 1 ; if ( ! isNumber ( participantCount ) ) throw new TypeError ( "Number expected: participantCount." ) ; if ( ( participantCount |= 0 ) <= 0 ) throw new RangeError ( "Argument out of range: participantCount." ) ; if ( this . _isExecutingPostPhaseAction ) throw new Error ( "This method may not be called from within the postPhaseAction." ) ; this . _participantCount += participantCount ; this . _remainingParticipants += participantCount ; } param participantCount remove ( participantCount ? ) { if ( isMissing ( participantCount ) ) participantCount = 1 ; if ( ! isNumber ( participantCount ) ) throw new TypeError ( "Number expected: participantCount." ) ; if ( ( participantCount |= 0 ) <= 0 ) throw new RangeError ( "Argument out of range: participantCount." ) ; if ( this . _participantCount < participantCount ) throw new RangeError ( "Argument out of range: participantCount." ) ; if ( this . _isExecutingPostPhaseAction ) throw new Error ( "This method may not be called from within the postPhaseAction." ) ; this . _participantCount -= participantCount ; this . _remainingParticipants -= participantCount ; if ( this . _participantCount === 0 ) { this . _finishPhase ( ) ; } } param cancelable async signalAndWait ( cancelable ? ) { Cancelable . throwIfSignaled ( cancelable ) ; if ( this . _isExecutingPostPhaseAction ) throw new Error ( "This method may not be called from within the postPhaseAction." ) ; if ( this . _participantCount === 0 ) throw new Error ( "The barrier has no registered participants." ) ; if ( this . _remainingParticipants === 0 ) throw new Error ( "The number of operations using the barrier exceeded the number of registered participants." ) ; const waiter = this . _waiters . wait ( cancelable ) ; this . _remainingParticipants -- ; if ( this . _remainingParticipants === 0 ) { this . _finishPhase ( ) ; } await waiter ; } private _finishPhase ( ) { const postPhaseAction = this . _postPhaseAction ; if ( postPhaseAction ) { this . _isExecutingPostPhaseAction = true ; Promise . resolve ( ) . then ( ( ) => postPhaseAction ( this ) ) . then ( ( ) => this . _resolveNextPhase ( ) , error => this . _rejectNextPhase ( error ) ) ; } else { Promise . resolve ( ) . then ( ( ) => this . _resolveNextPhase ( ) ) ; } } private _nextPhase ( ) { this . _isExecutingPostPhaseAction = false ; this . _remainingParticipants = this . _participantCount ; this . _phaseNumber ++ ; } private _resolveNextPhase ( ) { this . _nextPhase ( ) ; this . _waiters . resolveAll ( ) ; } private _rejectNextPhase ( error ) { this . _nextPhase ( ) ; this . _waiters . rejectAll ( error ) ; } }