import type WebWorker from './worker-wrapper' ; import type { JobReturnCall , JobCall } from './worker-wrapper' ; import calculateMedian from './median' ; import { nanoid } from 'nanoid' ; export default class Scheduler { workerPool : Map < string , WebWorker > = new Map ( ) ; pressure = 0 ; lastJobsExecTimes : number [ ] = [ ] ; readonly #jobsExecTimesBacklogSize = 20 ; async extendPool ( worker ) { const id = nanoid ( ) ; this . workerPool . set ( id , worker ) ; return id ; } async reducePool ( wid ? ) { let worker , id ; if ( wid ) { [ worker , id ] = [ this . workerPool . get ( wid ) , wid ] ; } else { ( { worker , wid : id } = this . getLeastBusyWorker ( ) ) ; } if ( ! worker ) return ; this . workerPool . delete ( id ) ; await worker . shutdown ( ) ; } async submitJob < T > ( message , transfer ? ) { const id = nanoid ( ) ; const job = { type : 'job' , id , message , transfer } ; const { worker } = this . getLeastBusyWorker ( ) ; worker . submitJob ( job ) ; this . pressure ++ ; await worker . waitFor ( `jobok-${ id }` ) ; const perfMarkA = performance . now ( ) ; const jobResults = await worker . waitFor < JobReturnCall < T > > ( `jobdone-${ id }` ) ; this . pressure -- ; const perfMarkB = performance . now ( ) ; this . lastJobsExecTimes . unshift ( perfMarkB - perfMarkA ) ; this . lastJobsExecTimes . splice ( this . #jobsExecTimesBacklogSize , this . lastJobsExecTimes . length - this . #jobsExecTimesBacklogSize ) ; if ( jobResults . error ) { throw new Error ( '(in Rinzler job): ' + jobResults . message ) ; } return jobResults . message ; } measureMedianExecTime ( ) { return calculateMedian ( this . lastJobsExecTimes ) ; } getLeastBusyWorker ( ) { let lowest = [ - 1 , '' ] ; for ( const entry of this . workerPool ) { if ( lowest [ 0 ] === - 1 || entry [ 1 ] . jobs . length < lowest [ 0 ] ) { if ( ! entry [ 1 ] . active ) continue ; lowest = [ entry [ 1 ] . jobs . length , entry [ 0 ] ] ; if ( lowest [ 0 ] === 0 ) break ; } } return { worker : this . workerPool . get ( lowest [ 1 ] ) as WebWorker , wid : lowest [ 1 ] , jobCount : lowest [ 0 ] } ; } }