@@ -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
@@ -226,6 +296,7 @@ var LibraryPThread = {
226296 // Note: worker is intentionally not terminated so the pool can
227297 // dynamically grow.
228298 PThread . unusedWorkers . push ( worker ) ;
299+
229300 // Not a running Worker anymore
230301 // Detach the worker from the pthread object, and return it to the
231302 // worker pool as an unused worker.
@@ -265,6 +336,37 @@ var LibraryPThread = {
265336 // module loaded.
266337 PThread . tlsInitFunctions . forEach ( ( f ) => f ( ) ) ;
267338 } ,
339+ #if PTHREAD_MANAGER
340+ initManagerWorker : ( worker ) = > new Promise ( ( onFinishedLoading ) => {
341+ worker . onmessage = ( e ) => {
342+ var d = e . data ;
343+ var cmd = d . cmd ;
344+ switch ( cmd ) {
345+ case { { { CMD_FORWARD_FROM_WORKER } } } :
346+ PThread . proxyWorkers [ d . workerID ] ?. onmessage ( { data : d . msg } ) ;
347+ break ;
348+ case { { { CMD_ERROR_FROM_WORKER } } } :
349+ PThread . proxyWorkers [ d . workerID ] ?. onerror ( d . error ) ;
350+ break ;
351+ case { { { CMD_LOADED } } } :
352+ onFinishedLoading ( worker ) ;
353+ break ;
354+ default :
355+ if ( cmd ) err ( `manager worker sent an unknown command ${ cmd } ` ) ;
356+ }
357+ } ;
358+ worker . onerror = ( e ) => {
359+ err ( `Manager worker sent an error! ${ e . filename } :${ e . lineno } : ${ e . message } ` ) ;
360+ throw e ;
361+ } ;
362+ worker . postMessage ( {
363+ cmd : { { { CMD_LOAD_MANAGER } } } ,
364+ #if ASSERTIONS
365+ workerID : worker . workerID ,
366+ #endif
367+ } ) ;
368+ } ) ,
369+ #endif
268370 // Loads the WebAssembly module into the given Worker.
269371 // onFinishedLoading: A callback function that will be called once all of
270372 // the workers have been initialized and are
@@ -442,7 +544,7 @@ var LibraryPThread = {
442544 } ) ,
443545
444546#if PTHREAD_POOL_SIZE
445- async loadWasmModuleToAllWorkers ( ) {
547+ loadWasmModuleToAllWorkers ( ) {
446548 // Instantiation is synchronous in pthreads.
447549 if (
448550 ENVIRONMENT_IS_PTHREAD
@@ -468,7 +570,7 @@ var LibraryPThread = {
468570#endif // PTHREAD_POOL_SIZE
469571
470572 // Creates a new web Worker and places it in the unused worker pool to wait for its use.
471- allocateUnusedWorker ( ) {
573+ createRealWorker ( ) {
472574 var worker ;
473575#if EXPORT_ES6
474576 // If we're using module output, use bundler-friendly pattern.
@@ -541,11 +643,33 @@ var LibraryPThread = {
541643#endif
542644 worker = new Worker ( pthreadMainJs , { { { pthreadWorkerOptions } } } ) ;
543645#endif // EXPORT_ES6
544- #if ASSERTIONS
646+ #if ASSERTIONS || PTHREAD_MANAGER
545647 worker . workerID = PThread . nextWorkerID ++ ;
546648#endif
649+ return worker ;
650+ } ,
651+
652+ allocateUnusedWorker ( ) {
653+ var worker ;
654+ #if PTHREAD_MANAGER
655+ if ( PThread . managerWorker ) {
656+ var workerID = PThread . nextWorkerID ++ ;
657+ worker = new PThread . ProxyWorker ( workerID ) ;
658+ } else
659+ #endif
660+ {
661+ worker = PThread . createRealWorker ( ) ;
662+ }
547663 PThread . unusedWorkers . push ( worker ) ;
548664 } ,
665+ #if PTHREAD_POOL_SIZE
666+ spawnPool ( ) {
667+ var pthreadPoolSize = { { { PTHREAD_POOL_SIZE } } } ;
668+ while ( pthreadPoolSize -- ) {
669+ PThread . allocateUnusedWorker ( ) ;
670+ }
671+ } ,
672+ #endif
549673
550674 getNewWorker ( ) {
551675 if ( PThread . unusedWorkers . length == 0 ) {
@@ -697,6 +821,8 @@ var LibraryPThread = {
697821 assert ( threadParams . pthread_ptr , 'spawnThread called with null pthread ptr' ) ;
698822#endif
699823
824+
825+
700826 var worker = PThread . getNewWorker ( ) ;
701827 if ( ! worker ) {
702828 // No available workers in the PThread pool.
0 commit comments