import { Disposable } from "@esfx/disposable" ; import { Lockable } from "@esfx/threading-lockable" ; const CONDVAR_ID = 1 << 19 ; const CONDVAR_EXCLUDES = ~ ( CONDVAR_ID ) ; const ATOM_INDEX = 0 ; const kArray = Symbol ( "kArray" ) ; export class ConditionVariable implements Disposable { static readonly SIZE = 4 ; private [ kArray ] : Int32Array | undefined ; constructor ( buffer ? , byteOffset = 0 ) { let array ; if ( buffer instanceof SharedArrayBuffer ) { if ( byteOffset < 0 || byteOffset > buffer . byteLength - 4 ) throw new RangeError ( "Out of range: byteOffset." ) ; if ( byteOffset % 4 ) throw new RangeError ( "Not aligned: byteOffset." ) ; array = new Int32Array ( buffer , byteOffset , 1 ) ; } else { array = new Int32Array ( new SharedArrayBuffer ( 4 ) ) ; } Atomics . compareExchange ( array , ATOM_INDEX , 0 , CONDVAR_ID ) ; const data = Atomics . load ( array , ATOM_INDEX ) ; if ( ! ( data & CONDVAR_ID ) || data & CONDVAR_EXCLUDES ) throw new TypeError ( "Invalid handle." ) ; this [ kArray ] = array ; } /**
     * Gets the `SharedArrayBuffer` for this object.
     */ get buffer ( ) { const array = this [ kArray ] ; if ( ! array ) throw new ReferenceError ( "Object is disposed." ) ; return array . buffer as SharedArrayBuffer ; } /**
     * Gets the byte offset of this object in its buffer.
     */ get byteOffset ( ) { const array = this [ kArray ] ; if ( ! array ) throw new ReferenceError ( "Object is disposed." ) ; return array . byteOffset ; } /**
     * Gets the number of bytes occupied by this object in its buffer.
     */ get byteLength ( ) { const array = this [ kArray ] ; if ( ! array ) throw new ReferenceError ( "Object is disposed." ) ; return 4 ; } param mutex param condition returns wait ( mutex , condition ? ) { return this . waitFor ( mutex , Infinity , condition ) ; } param mutex param ms param condition returns waitFor ( mutex , ms , condition ? ) { if ( typeof ms !== "number" ) throw new TypeError ( "Number expected: ms" ) ; ms = isNaN ( ms ) ? Infinity : Math . max ( ms , 0 ) ; const array = this [ kArray ] ; if ( ! array ) throw new ReferenceError ( "Object is disposed." ) ; const start = isFinite ( ms ) ? Date . now ( ) : 0 ; let timeout = ms ; while ( ! condition ?. ( ) ) { if ( ! mutex [ Lockable . unlock ] ( ) ) throw new Error ( "Mutex does not own lock." ) ; const result = Atomics . wait ( array , ATOM_INDEX , CONDVAR_ID , timeout ) ; mutex [ Lockable . lock ] ( ) ; if ( result === "not-equal" ) throw new Error ( "Illegal state" ) ; if ( result === "timed-out" ) return false ; if ( ! condition ) break ; if ( isFinite ( ms ) ) { timeout = ms - ( Date . now ( ) - start ) ; } } return true ; } returns notifyOne ( ) { const array = this [ kArray ] ; if ( ! array ) throw new ReferenceError ( "Object is disposed." ) ; return Atomics . notify ( array , ATOM_INDEX , 1 ) === 1 ; } returns notifyAll ( ) { const array = this [ kArray ] ; if ( ! array ) throw new ReferenceError ( "Object is disposed." ) ; return Atomics . notify ( array , ATOM_INDEX , + Infinity ) ; } /**
     * Releases the resources associated for this object.
     */ close ( ) { this [ kArray ] = undefined ; } /**
     * Releases the resources associated for this object.
     */ [ Disposable . dispose ] ( ) { this . close ( ) ; } }