@@ -50,7 +50,7 @@ var LibraryPThread = {
50
50
Atomics . store ( HEAPU32 , ( pthreadPtr + { { { C_STRUCTS . pthread . profilerBlock } } } ) >> 2 , profilerBlock ) ;
51
51
52
52
// Zero fill contents at startup.
53
- for ( var i = 0 ; i < { { { C_STRUCTS . thread_profiler_block . __size__ } } } ; i += 4 ) Atomics . store ( HEAPU32 , ( profilerBlock + i ) >> 2 , 0 ) ;
53
+ for ( var i = 0 ; i < { { { C_STRUCTS . thread_profiler_block . __size__ } } } ; i += 4 ) Atomics . store ( HEAPU32 , ( profilerBlock + i ) >> 2 , 0 ) ;
54
54
Atomics . store ( HEAPU32 , ( pthreadPtr + { { { C_STRUCTS . thread_profiler_block . currentStatusStartTime } } } ) >> 2 , performance . now ( ) ) ;
55
55
} ,
56
56
@@ -149,6 +149,31 @@ var LibraryPThread = {
149
149
__register_pthread_ptr ( 0 , 0 , 0 ) ; // Unregister the thread block also inside the asm.js scope.
150
150
threadInfoStruct = 0 ;
151
151
if ( ENVIRONMENT_IS_PTHREAD ) {
152
+ // This worker no longer owns any WebGL OffscreenCanvases, so transfer them back to parent thread.
153
+ var transferList = [ ] ;
154
+
155
+ #if OFFSCREENCANVAS_SUPPORT
156
+ var offscreenCanvases = { } ;
157
+ if ( typeof GL !== 'undefined' ) {
158
+ offscreenCanvases = GL . offscreenCanvases ;
159
+ GL . offscreenCanvases = { } ;
160
+ }
161
+ for ( var i in offscreenCanvases ) {
162
+ if ( offscreenCanvases [ i ] ) transferList . push ( offscreenCanvases [ i ] ) ;
163
+ }
164
+ if ( transferList . length > 0 ) {
165
+ postMessage ( {
166
+ targetThread : parentThreadId ,
167
+ cmd : 'objectTransfer' ,
168
+ offscreenCanvases : offscreenCanvases ,
169
+ moduleCanvasId : Module [ 'canvas' ] . id , // moduleCanvasId specifies which canvas is denoted via the "#canvas" shorthand.
170
+ transferList : transferList
171
+ } , transferList ) ;
172
+ }
173
+ // And clear the OffscreenCanvases from lingering around in this Worker as well.
174
+ delete Module [ 'canvas' ] ;
175
+ #endif
176
+
152
177
postMessage ( { cmd : 'exit' } ) ;
153
178
}
154
179
}
@@ -204,6 +229,18 @@ var LibraryPThread = {
204
229
if ( pthread . worker ) pthread . worker . pthread = null ;
205
230
} ,
206
231
232
+ receiveObjectTransfer : function ( data ) {
233
+ #if OFFSCREENCANVAS_SUPPORT
234
+ if ( typeof GL !== 'undefined' ) {
235
+ for ( var i in data . offscreenCanvases ) {
236
+ GL . offscreenCanvases [ i ] = data . offscreenCanvases [ i ] ;
237
+ GL . offscreenCanvases [ i ] . id = i ; // https://bugzilla.mozilla.org/show_bug.cgi?id=1281909
238
+ }
239
+ if ( ! Module [ 'canvas' ] ) Module [ 'canvas' ] = GL . offscreenCanvases [ data . moduleCanvasId ] ;
240
+ }
241
+ #endif
242
+ } ,
243
+
207
244
// Allocates the given amount of new web workers and stores them in the pool of unused workers.
208
245
// onFinishedLoading: A callback function that will be called once all of the workers have been initialized and are
209
246
// ready to host pthreads. Optional. This is used to mitigate bug https://bugzilla.mozilla.org/show_bug.cgi?id=1049079
@@ -222,6 +259,17 @@ var LibraryPThread = {
222
259
var worker = new Worker ( pthreadMainJs ) ;
223
260
224
261
worker . onmessage = function ( e ) {
262
+ // If this message is intended to a recipient that is not the main thread, forward it to the target thread.
263
+ if ( e . data . targetThread && e . data . targetThread != _pthread_self ( ) ) {
264
+ var thread = PThread . pthreads [ e . data . targetThread ] ;
265
+ if ( thread ) {
266
+ thread . worker . postMessage ( e . data , e . data . transferList ) ;
267
+ } else {
268
+ console . error ( 'Internal error! Worker sent a message "' + e . data . cmd + '" to target pthread ' + e . data . targetThread + ', but that thread no longer exists!' ) ;
269
+ }
270
+ return ;
271
+ }
272
+
225
273
if ( e . data . cmd === 'processQueuedMainThreadWork' ) {
226
274
// TODO: Must post message to main Emscripten thread in PROXY_TO_WORKER mode.
227
275
_emscripten_main_thread_process_queued_calls ( ) ;
@@ -242,16 +290,18 @@ var LibraryPThread = {
242
290
Module [ 'print' ] ( 'Thread ' + e . data . threadId + ': ' + e . data . text ) ;
243
291
} else if ( e . data . cmd === 'printErr' ) {
244
292
Module [ 'printErr' ] ( 'Thread ' + e . data . threadId + ': ' + e . data . text ) ;
245
- } else if ( e . data . cmd == 'alert' ) {
293
+ } else if ( e . data . cmd === 'alert' ) {
246
294
alert ( 'Thread ' + e . data . threadId + ': ' + e . data . text ) ;
247
295
} else if ( e . data . cmd === 'exit' ) {
248
- // todo
296
+ // currently no-op
249
297
} else if ( e . data . cmd === 'cancelDone' ) {
250
- PThread . freeThreadData ( worker . pthread ) ;
251
- worker . pthread = undefined ; // Detach the worker from the pthread object, and return it to the worker pool as an unused worker.
252
- PThread . unusedWorkerPool . push ( worker ) ;
253
- // TODO: Free if detached.
254
- PThread . runningWorkers . splice ( PThread . runningWorkers . indexOf ( worker . pthread ) , 1 ) ; // Not a running Worker anymore.
298
+ PThread . freeThreadData ( worker . pthread ) ;
299
+ worker . pthread = undefined ; // Detach the worker from the pthread object, and return it to the worker pool as an unused worker.
300
+ PThread . unusedWorkerPool . push ( worker ) ;
301
+ // TODO: Free if detached.
302
+ PThread . runningWorkers . splice ( PThread . runningWorkers . indexOf ( worker . pthread ) , 1 ) ; // Not a running Worker anymore.
303
+ } else if ( e . data . cmd === 'objectTransfer' ) {
304
+ PThread . receiveObjectTransfer ( e . data ) ;
255
305
} else {
256
306
Module [ 'printErr' ] ( "worker sent an unknown command " + e . data . cmd ) ;
257
307
}
@@ -375,9 +425,14 @@ var LibraryPThread = {
375
425
arg : threadParams . arg ,
376
426
threadInfoStruct : threadParams . pthread_ptr ,
377
427
selfThreadId : threadParams . pthread_ptr , // TODO: Remove this since thread ID is now the same as the thread address.
428
+ parentThreadId : threadParams . parent_pthread_ptr ,
378
429
stackBase : threadParams . stackBase ,
379
- stackSize : threadParams . stackSize
380
- } ) ;
430
+ stackSize : threadParams . stackSize ,
431
+ #if OFFSCREENCANVAS_SUPPORT
432
+ moduleCanvasId : threadParams . moduleCanvasId ,
433
+ offscreenCanvases : threadParams . offscreenCanvases ,
434
+ #endif
435
+ } , threadParams . transferList ) ;
381
436
} ,
382
437
383
438
#if USE_PTHREADS
@@ -406,8 +461,6 @@ var LibraryPThread = {
406
461
407
462
pthread_create__deps : [ '_spawn_thread' , 'pthread_getschedparam' , 'pthread_self' ] ,
408
463
pthread_create : function ( pthread_ptr , attr , start_routine , arg ) {
409
- if ( ENVIRONMENT_IS_PTHREAD ) return _emscripten_sync_run_in_main_thread_4 ( { { { cDefine ( 'EM_PROXIED_PTHREAD_CREATE' ) } } } , pthread_ptr , attr , start_routine , arg ) ;
410
-
411
464
if ( typeof SharedArrayBuffer === 'undefined' ) {
412
465
Module [ 'printErr' ] ( 'Current environment does not support SharedArrayBuffer, pthreads are not available!' ) ;
413
466
return { { { cDefine ( 'EAGAIN' ) } } } ;
@@ -416,6 +469,65 @@ var LibraryPThread = {
416
469
Module [ 'printErr' ] ( 'pthread_create called with a null thread pointer!' ) ;
417
470
return { { { cDefine ( 'EINVAL' ) } } } ;
418
471
}
472
+
473
+ var transferList = [ ] ; // List of JS objects that will transfer ownership to the Worker hosting the thread
474
+
475
+ #if OFFSCREENCANVAS_SUPPORT
476
+ // Deduce which WebGL canvases (HTMLCanvasElements or OffscreenCanvases) should be passed over to the
477
+ // Worker that hosts the spawned pthread.
478
+ var transferredCanvasNames = { { { makeGetValue ( 'attr' , 36 , 'i32' ) } } } ; // Comma-delimited list of IDs "canvas1, canvas2, ..."
479
+ if ( transferredCanvasNames ) transferredCanvasNames = Pointer_stringify ( transferredCanvasNames ) . split ( ',' ) ;
480
+
481
+ var offscreenCanvases = { } ; // Dictionary of OffscreenCanvas objects we'll transfer to the created thread to own
482
+ var moduleCanvasId = Module [ 'canvas' ] ? Module [ 'canvas' ] . id : '' ;
483
+ for ( var i in transferredCanvasNames ) {
484
+ var name = transferredCanvasNames [ i ] . trim ( ) ;
485
+ var offscreenCanvas ;
486
+ try {
487
+ if ( name == '#canvas' ) {
488
+ if ( ! Module [ 'canvas' ] ) {
489
+ Module [ 'printErr' ] ( 'pthread_create: could not find canvas with ID "' + name + '" to transfer to thread!' ) ;
490
+ return { { { cDefine ( 'EINVAL' ) } } } ;
491
+ }
492
+ name = Module [ 'canvas' ] . id ;
493
+ }
494
+ if ( GL . offscreenCanvases [ name ] ) {
495
+ offscreenCanvas = GL . offscreenCanvases [ name ] ;
496
+ GL . offscreenCanvases [ name ] = null ; // This thread no longer owns this canvas.
497
+ if ( Module [ 'canvas' ] instanceof OffscreenCanvas && name === Module [ 'canvas' ] . id ) Module [ 'canvas' ] = null ;
498
+ } else {
499
+ var canvas = ( Module [ 'canvas' ] && Module [ 'canvas' ] . id === name ) ? Module [ 'canvas' ] : document . getElementByID ( name ) ;
500
+ if ( ! canvas ) {
501
+ Module [ 'printErr' ] ( 'pthread_create: could not find canvas with ID "' + name + '" to transfer to thread!' ) ;
502
+ return { { { cDefine ( 'EINVAL' ) } } } ;
503
+ }
504
+ if ( canvas . controlTransferredOffscreen ) {
505
+ Module [ 'printErr' ] ( 'pthread_create: cannot transfer canvas with ID "' + name + '" to thread, since the current thread does not have control over it!' ) ;
506
+ return { { { cDefine ( 'EPERM' ) } } } ; // Operation not permitted, some other thread is accessing the canvas.
507
+ }
508
+ if ( ! canvas . transferControlToOffscreen ) {
509
+ Module [ 'printErr' ] ( 'pthread_create: cannot transfer control of canvas "' + name + '" to pthread, because current browser does not support OffscreenCanvas!' ) ;
510
+ return { { { cDefine ( 'ENOSYS' ) } } } ; // Function not implemented, browser doesn't have support for this.
511
+ }
512
+ offscreenCanvas = canvas . transferControlToOffscreen ( ) ;
513
+ canvas . controlTransferredOffscreen = true ;
514
+ offscreenCanvas . id = canvas . id ;
515
+ }
516
+ transferList . push ( offscreenCanvas ) ;
517
+ offscreenCanvases [ offscreenCanvas . id ] = offscreenCanvas ;
518
+ } catch ( e ) {
519
+ Module [ 'printErr' ] ( 'pthread_create: failed to transfer control of canvas "' + name + '" to OffscreenCanvas! Error: ' + e ) ;
520
+ return { { { cDefine ( 'EINVAL' ) } } } ; // Hitting this might indicate an implementation bug or some other internal error
521
+ }
522
+ }
523
+ #endif
524
+
525
+ // Synchronously proxy the thread creation to main thread if possible. If we need to transfer ownership of objects, then
526
+ // proxy asynchronously via postMessage.
527
+ if ( ENVIRONMENT_IS_PTHREAD && transferList . length == 0 ) {
528
+ return _emscripten_sync_run_in_main_thread_4 ( { { { cDefine ( 'EM_PROXIED_PTHREAD_CREATE' ) } } } , pthread_ptr , attr , start_routine , arg ) ;
529
+ }
530
+
419
531
var stackSize = 0 ;
420
532
var stackBase = 0 ;
421
533
var detached = 0 ; // Default thread attr is PTHREAD_CREATE_JOINABLE, i.e. start as not detached.
@@ -468,14 +580,20 @@ var LibraryPThread = {
468
580
detached : detached ,
469
581
startRoutine : start_routine ,
470
582
pthread_ptr : threadInfoStruct ,
583
+ parent_pthread_ptr : _pthread_self ( ) ,
471
584
arg : arg ,
585
+ #if OFFSCREENCANVAS_SUPPORT
586
+ moduleCanvasId : moduleCanvasId ,
587
+ offscreenCanvases : offscreenCanvases ,
588
+ #endif
589
+ transferList : transferList
472
590
} ;
473
591
474
592
if ( ENVIRONMENT_IS_PTHREAD ) {
475
593
// The prepopulated pool of web workers that can host pthreads is stored in the main JS thread. Therefore if a
476
594
// pthread is attempting to spawn a new thread, the thread creation must be deferred to the main JS thread.
477
595
threadParams . cmd = 'spawnThread' ;
478
- postMessage ( threadParams ) ;
596
+ postMessage ( threadParams , transferList ) ;
479
597
} else {
480
598
// We are the main thread, so we have the pthread warmup pool in this thread and can fire off JS thread creation
481
599
// directly ourselves.
0 commit comments