@@ -40,6 +40,14 @@ const CMD_CLEANUP_THREAD = 6;
4040const CMD_MARK_AS_FINISHED = 7 ;
4141const CMD_UNCAUGHT_EXN = 8 ;
4242const CMD_CALL_HANDLER = 9 ;
43+ #if PTHREAD_MANAGER
44+ const CMD_LOAD_MANAGER = 10 ;
45+ const CMD_CREATE_WORKER = 11 ;
46+ const CMD_TERMINATE_WORKER = 12 ;
47+ const CMD_FORWARD_TO_WORKER = 13 ;
48+ const CMD_FORWARD_FROM_WORKER = 14 ;
49+ const CMD_ERROR_FROM_WORKER = 15 ;
50+ #endif
4351
4452#if WASM_ESM_INTEGRATION
4553const pthreadWorkerScript = TARGET_BASENAME + '.pthread.mjs' ;
@@ -113,13 +121,48 @@ var LibraryPThread = {
113121 // the reverse mapping, each worker has a `pthread_ptr` when its running a
114122 // pthread.
115123 pthreads : { } ,
124+ #if PTHREAD_MANAGER
125+ proxyWorkers : { } ,
126+ // A ProxyWorker acts as a main-thread representative for a Web Worker
127+ // managed by the manager worker. It implements the standard Worker
128+ // interface (postMessage, terminate, onmessage, onerror) so that the main
129+ // thread's scheduler can interact with it transparently and pool it just
130+ // like a standard worker.
131+ ProxyWorker : class {
132+ constructor ( workerID ) {
133+ this . workerID = workerID ;
134+ PThread . proxyWorkers [ workerID ] = this ;
135+ PThread . managerWorker . postMessage ( {
136+ cmd : { { { CMD_CREATE_WORKER } } } ,
137+ workerID : workerID
138+ } ) ;
139+ }
140+ postMessage ( msg , transfer ) {
141+ PThread . managerWorker . postMessage ( {
142+ cmd : { { { CMD_FORWARD_TO_WORKER } } } ,
143+ workerID : this . workerID ,
144+ msg : msg ,
145+ transferList : transfer
146+ } , transfer ) ;
147+ }
148+ terminate ( ) {
149+ PThread . managerWorker . postMessage ( {
150+ cmd : { { { CMD_TERMINATE_WORKER } } } ,
151+ workerID : this . workerID
152+ } ) ;
153+ delete PThread . proxyWorkers [ this . workerID ] ;
154+ }
155+ } ,
156+ #endif
116157#if MAIN_MODULE
117158 outstandingPromises : { } ,
118159 // Finished threads are threads that have finished running but we are not yet
119160 // joined.
120161 finishedThreads : new Set ( ) ,
121162#endif
122- #if ASSERTIONS
163+ #if ASSERTIONS || PTHREAD_MANAGER
164+ // Used for debugging/assertions, or functionally by PTHREAD_MANAGER to
165+ // route multiplexed messages/errors to the correct ProxyWorker instance.
123166 nextWorkerID : 1 ,
124167#endif
125168 init ( ) {
@@ -128,19 +171,46 @@ var LibraryPThread = {
128171 }
129172 } ,
130173 initMainThread ( ) {
174+ #if ENVIRONMENT_MAY_BE_NODE
175+ if ( ENVIRONMENT_IS_NODE ) return ;
176+ #endif
177+ #if PTHREAD_MANAGER
178+ #if ASSERTIONS
179+ dbg ( 'PThread: initializing manager worker' ) ;
180+ #endif
181+ var managerReadyResolve ;
182+ PThread . managerWorkerReady = new Promise ( ( resolve ) => { managerReadyResolve = resolve ; } ) ;
183+ PThread . managerWorker = PThread . createRealWorker ( ) ;
184+ addOnPreRun ( async ( ) => {
185+ var managerReady = PThread . initManagerWorker ( PThread . managerWorker ) ;
186+ addRunDependency ( 'manager-worker' ) ;
187+ await managerReady ;
131188#if PTHREAD_POOL_SIZE
132- var pthreadPoolSize = { { { PTHREAD_POOL_SIZE } } } ;
133- // Start loading up the Worker pool, if requested.
134- while ( pthreadPoolSize -- ) {
135- PThread . allocateUnusedWorker ( ) ;
136- }
189+ PThread . spawnPool ( ) ;
190+ #endif
191+ removeRunDependency ( 'manager-worker' ) ;
192+ managerReadyResolve ( ) ;
193+ } ) ;
194+ #endif // PTHREAD_MANAGER
195+ #if PTHREAD_POOL_SIZE
196+ #if ASSERTIONS
197+ dbg ( 'PThread: initializing worker pool' ) ;
198+ #endif
199+ #if ! PTHREAD_MANAGER
200+ PThread . spawnPool ( ) ;
201+ #endif
137202#if ! MINIMAL_RUNTIME
138203 // MINIMAL_RUNTIME takes care of calling loadWasmModuleToAllWorkers
139204 // in postamble_minimal.js
140205 addOnPreRun ( async ( ) => {
141- var pthreadPoolReady = PThread . loadWasmModuleToAllWorkers ( ) ;
142206#if ! PTHREAD_POOL_DELAY_LOAD
143207 addRunDependency ( 'loading-workers' ) ;
208+ #endif
209+ #if PTHREAD_MANAGER
210+ await PThread . managerWorkerReady ;
211+ #endif
212+ var pthreadPoolReady = PThread . loadWasmModuleToAllWorkers ( ) ;
213+ #if ! PTHREAD_POOL_DELAY_LOAD
144214 await pthreadPoolReady ;
145215 removeRunDependency ( 'loading-workers' ) ;
146216#endif // PTHREAD_POOL_DELAY_LOAD
@@ -265,6 +335,37 @@ var LibraryPThread = {
265335 // module loaded.
266336 PThread . tlsInitFunctions . forEach ( ( f ) => f ( ) ) ;
267337 } ,
338+ #if PTHREAD_MANAGER
339+ initManagerWorker : ( worker ) = > new Promise ( ( onFinishedLoading ) => {
340+ worker . onmessage = ( e ) => {
341+ var d = e . data ;
342+ var cmd = d . cmd ;
343+ switch ( cmd ) {
344+ case { { { CMD_FORWARD_FROM_WORKER } } } :
345+ PThread . proxyWorkers [ d . workerID ] ?. onmessage ( { data : d . msg } ) ;
346+ break ;
347+ case { { { CMD_ERROR_FROM_WORKER } } } :
348+ PThread . proxyWorkers [ d . workerID ] ?. onerror ( d . error ) ;
349+ break ;
350+ case { { { CMD_LOADED } } } :
351+ onFinishedLoading ( worker ) ;
352+ break ;
353+ default :
354+ if ( cmd ) err ( `manager worker sent an unknown command ${ cmd } ` ) ;
355+ }
356+ } ;
357+ worker . onerror = ( e ) => {
358+ err ( `Manager worker sent an error! ${ e . filename } :${ e . lineno } : ${ e . message } ` ) ;
359+ throw e ;
360+ } ;
361+ worker . postMessage ( {
362+ cmd : { { { CMD_LOAD_MANAGER } } } ,
363+ #if ASSERTIONS
364+ workerID : worker . workerID ,
365+ #endif
366+ } ) ;
367+ } ) ,
368+ #endif
268369 // Loads the WebAssembly module into the given Worker.
269370 // onFinishedLoading: A callback function that will be called once all of
270371 // the workers have been initialized and are
@@ -442,7 +543,7 @@ var LibraryPThread = {
442543 } ) ,
443544
444545#if PTHREAD_POOL_SIZE
445- async loadWasmModuleToAllWorkers ( ) {
546+ loadWasmModuleToAllWorkers ( ) {
446547 // Instantiation is synchronous in pthreads.
447548 if (
448549 ENVIRONMENT_IS_PTHREAD
@@ -468,7 +569,7 @@ var LibraryPThread = {
468569#endif // PTHREAD_POOL_SIZE
469570
470571 // Creates a new web Worker and places it in the unused worker pool to wait for its use.
471- allocateUnusedWorker ( ) {
572+ createRealWorker ( ) {
472573 var worker ;
473574#if EXPORT_ES6
474575 // If we're using module output, use bundler-friendly pattern.
@@ -541,12 +642,34 @@ var LibraryPThread = {
541642#endif
542643 worker = new Worker ( pthreadMainJs , { { { pthreadWorkerOptions } } } ) ;
543644#endif // EXPORT_ES6
544- #if ASSERTIONS
645+ #if ASSERTIONS || PTHREAD_MANAGER
545646 worker . workerID = PThread . nextWorkerID ++ ;
546647#endif
648+ return worker ;
649+ } ,
650+
651+ allocateUnusedWorker ( ) {
652+ var worker ;
653+ #if PTHREAD_MANAGER
654+ if ( PThread . managerWorker ) {
655+ var workerID = PThread . nextWorkerID ++ ;
656+ worker = new PThread . ProxyWorker ( workerID ) ;
657+ } else
658+ #endif
659+ {
660+ worker = PThread . createRealWorker ( ) ;
661+ }
547662 PThread . unusedWorkers . push ( worker ) ;
548663 return worker ;
549664 } ,
665+ #if PTHREAD_POOL_SIZE
666+ spawnPool ( ) {
667+ var pthreadPoolSize = { { { PTHREAD_POOL_SIZE } } } ;
668+ while ( pthreadPoolSize -- ) {
669+ PThread . allocateUnusedWorker ( ) ;
670+ }
671+ } ,
672+ #endif
550673
551674 getNewWorker ( ) {
552675 if ( PThread . unusedWorkers . length == 0 ) {
@@ -698,6 +821,8 @@ var LibraryPThread = {
698821 assert ( threadParams . pthread_ptr , 'spawnThread called with null pthread ptr' ) ;
699822#endif
700823
824+
825+
701826 var worker = PThread . getNewWorker ( ) ;
702827 if ( ! worker ) {
703828 // No available workers in the PThread pool.
0 commit comments