@@ -8,7 +8,7 @@ var LibraryPThread = {
8
8
$PThread__postset : 'if (!ENVIRONMENT_IS_PTHREAD) PThread.initMainThreadBlock();' ,
9
9
$PThread__deps : [ '_emscripten_thread_init' ,
10
10
'emscripten_register_main_browser_thread_id' ,
11
- '$ERRNO_CODES' , 'emscripten_futex_wake' , '$killThread' ,
11
+ '$ERRNO_CODES' , 'emscripten_futex_wake' ,
12
12
'$cancelThread' , '$cleanupThread'
13
13
#if USE_ASAN || USE_LSAN
14
14
, '$withBuiltinMalloc'
@@ -191,7 +191,7 @@ var LibraryPThread = {
191
191
Atomics . store ( HEAPU32 , ( tb + { { { C_STRUCTS . pthread . detach_state } } } ) >> 2 , { { { cDefine ( 'DT_EXITED' ) } } } ) ; // Mark the thread as no longer running.
192
192
193
193
// Wake any joiner.
194
- _emscripten_futex_wake ( tb + { { { C_STRUCTS . pthread . detach_state } } } , { { { cDefine ( 'INT_MAX' ) } } } ) ;
194
+ _emscripten_futex_wake ( tb + { { { C_STRUCTS . pthread . detach_state } } } , 1 ) ;
195
195
196
196
// Not hosting a pthread anymore in this worker, reset the info structures to null.
197
197
__emscripten_thread_init ( 0 , 0 , 0 ) ; // Unregister the thread block also inside the asm.js scope.
@@ -209,24 +209,24 @@ var LibraryPThread = {
209
209
if ( ENVIRONMENT_IS_PTHREAD ) {
210
210
// Note: in theory we would like to return any offscreen canvases back to the main thread,
211
211
// but if we ever fetched a rendering context for them that would not be valid, so we don't try.
212
- postMessage ( { 'cmd' : 'exit' } ) ;
212
+ postMessage ( { 'cmd' : 'cleanupThread' , 'thread' : tb , 'terminate' : false } ) ;
213
213
}
214
214
}
215
215
} ,
216
216
217
+ terminateAllThreads__deps : [ '$cleanupThread' ] ,
217
218
terminateAllThreads : function ( ) {
218
219
for ( var t in PThread . pthreads ) {
219
- var pthread = PThread . pthreads [ t ] ;
220
- if ( pthread && pthread . worker ) {
221
- PThread . returnWorkerToPool ( pthread . worker ) ;
220
+ if ( t && t . worker ) {
221
+ cleanupThread ( t , true ) ;
222
222
}
223
223
}
224
224
PThread . pthreads = { } ;
225
225
226
226
for ( var i = 0 ; i < PThread . unusedWorkers . length ; ++ i ) {
227
227
var worker = PThread . unusedWorkers [ i ] ;
228
228
#if ASSERTIONS
229
- assert ( ! worker . pthread ) ; // This Worker should not be hosting a pthread at this time.
229
+ assert ( ! worker . pthread , ' This Worker should not be hosting a pthread at this time' ) ;
230
230
#endif
231
231
worker . terminate ( ) ;
232
232
}
@@ -238,14 +238,14 @@ var LibraryPThread = {
238
238
#if ASSERTIONS
239
239
assert ( pthread , 'This Worker should have a pthread it is executing' ) ;
240
240
#endif
241
- PThread . freeThreadData ( pthread ) ;
242
- worker . terminate ( ) ;
241
+ cleanupThread ( pthread , true ) ;
243
242
}
244
243
PThread . runningWorkers = [ ] ;
245
244
} ,
246
245
freeThreadData : function ( pthread ) {
247
246
if ( ! pthread ) return ;
248
247
if ( pthread . threadInfoStruct ) {
248
+ // Free thread-specific data
249
249
var tlsMemory = { { { makeGetValue ( 'pthread.threadInfoStruct' , C_STRUCTS . pthread . tsd , 'i32' ) } } } ;
250
250
{ { { makeSetValue ( 'pthread.threadInfoStruct' , C_STRUCTS . pthread . tsd , 0 , 'i32' ) } } } ;
251
251
#if PTHREADS_PROFILING
@@ -261,19 +261,23 @@ var LibraryPThread = {
261
261
pthread . stackBase = 0 ;
262
262
if ( pthread . worker ) pthread . worker . pthread = null ;
263
263
} ,
264
- returnWorkerToPool: function ( worker ) {
264
+ returnWorkerToPool: function ( worker , terminate ) {
265
265
// We don't want to run main thread queued calls here, since we are doing
266
266
// some operations that leave the worker queue in an invalid state until
267
267
// we are completely done (it would be bad if free() ends up calling a
268
268
// queued pthread_create which looks at the global data structures we are
269
269
// modifying).
270
270
PThread . runWithoutMainThreadQueuedCalls ( function ( ) {
271
271
delete PThread . pthreads [ worker . pthread . threadInfoStruct ] ;
272
- //Note: worker is intentionally not terminated so the pool can dynamically grow.
273
- PThread . unusedWorkers . push ( worker ) ;
272
+ if ( terminate ) {
273
+ worker . terminate ( ) ;
274
+ } else {
275
+ // No termination needed? Return it to the worker pool as an unused worker.
276
+ PThread . unusedWorkers . push ( worker ) ;
277
+ }
274
278
PThread . runningWorkers . splice ( PThread . runningWorkers . indexOf ( worker ) , 1 ) ; // Not a running Worker anymore
275
279
PThread . freeThreadData ( worker . pthread ) ;
276
- // Detach the worker from the pthread object, and return it to the worker pool as an unused worker .
280
+ // Detach the worker from the pthread object.
277
281
worker . pthread = undefined ;
278
282
} ) ;
279
283
} ,
@@ -345,9 +349,7 @@ var LibraryPThread = {
345
349
} else if ( cmd === 'spawnThread' ) {
346
350
spawnThread ( e . data ) ;
347
351
} else if ( cmd === 'cleanupThread' ) {
348
- cleanupThread ( d [ 'thread' ] ) ;
349
- } else if ( cmd === 'killThread' ) {
350
- killThread ( d [ 'thread' ] ) ;
352
+ cleanupThread ( d [ 'thread' ] , d [ 'terminate' ] ) ;
351
353
} else if ( cmd === 'cancelThread' ) {
352
354
cancelThread ( d [ 'thread' ] ) ;
353
355
} else if ( cmd === 'loaded' ) {
@@ -364,12 +366,6 @@ var LibraryPThread = {
364
366
err ( 'Thread ' + d [ 'threadId' ] + ': ' + d [ 'text' ] ) ;
365
367
} else if ( cmd === 'alert' ) {
366
368
alert ( 'Thread ' + d [ 'threadId' ] + ': ' + d [ 'text' ] ) ;
367
- } else if ( cmd === 'exit' ) {
368
- // Detect whether the pthread_t structure is 'alive', it might have already been cleaned up
369
- var alive = worker . pthread && Atomics . load ( HEAPU32 , ( worker . pthread . threadInfoStruct + { { { C_STRUCTS . pthread . self } } } ) >> 2 ) ;
370
- if ( alive ) {
371
- PThread . returnWorkerToPool ( worker ) ;
372
- }
373
369
} else if ( cmd === 'exitProcess' ) {
374
370
// A pthread has requested to exit the whole application process (runtime).
375
371
#if ASSERTIONS
@@ -478,41 +474,28 @@ var LibraryPThread = {
478
474
}
479
475
} ,
480
476
481
- $killThread : function ( pthread_ptr ) {
482
- if ( ENVIRONMENT_IS_PTHREAD ) throw 'Internal Error! killThread() can only ever be called from main application thread!' ;
483
- if ( ! pthread_ptr ) throw 'Internal Error! Null pthread_ptr in killThread!' ;
484
- { { { makeSetValue ( 'pthread_ptr' , C_STRUCTS . pthread . self , 0 , 'i32' ) } } } ;
485
- var pthread = PThread . pthreads [ pthread_ptr ] ;
486
- pthread . worker . terminate ( ) ;
487
- PThread . freeThreadData ( pthread ) ;
488
- // The worker was completely nuked (not just the pthread execution it was hosting), so remove it from running workers
489
- // but don't put it back to the pool.
490
- PThread . runningWorkers . splice ( PThread . runningWorkers . indexOf ( pthread . worker ) , 1 ) ; // Not a running Worker anymore.
491
- pthread . worker . pthread = undefined ;
492
- } ,
493
-
494
- $cleanupThread : function ( pthread_ptr ) {
477
+ $cleanupThread : function ( pthread_ptr , terminate ) {
495
478
if ( ENVIRONMENT_IS_PTHREAD ) throw 'Internal Error! cleanupThread() can only ever be called from main application thread!' ;
496
479
if ( ! pthread_ptr ) throw 'Internal Error! Null pthread_ptr in cleanupThread!' ;
497
480
var pthread = PThread . pthreads [ pthread_ptr ] ;
498
- // If pthread has been removed from this map this also means that pthread_ptr points
499
- // to already freed data. Such situation may occur in following circumstances:
481
+ // Check whether pthread_ptr points to already freed data. Such situation may occur in
482
+ // following circumstances:
500
483
// 1. Joining cancelled thread - in such situation it may happen that pthread data will
501
484
// already be removed by handling 'cancelDone' message.
502
485
// 2. Joining thread from non-main browser thread (this also includes thread running main()
503
486
// when compiled with `PROXY_TO_PTHREAD`) - in such situation it may happen that following
504
487
// code flow occur (MB - Main Browser Thread, S1, S2 - Worker Threads):
505
- // S2: thread ends, 'exit ' message is sent to MB
488
+ // S2: thread ends, 'cleanupThread ' message is sent to MB
506
489
// S1: calls pthread_join(S2), this causes:
507
490
// a. S2 is marked as detached,
508
491
// b. 'cleanupThread' message is sent to MB.
509
- // MB: handles 'exit ' message, as thread is detached, so returnWorkerToPool()
510
- // is called and all thread related structs are freed/released.
511
- // MB: handles 'cleanupThread' message which calls this function.
512
- if ( pthread ) {
513
- { { { makeSetValue ( 'pthread_ptr' , C_STRUCTS . pthread . self , 0 , 'i32' ) } } } ;
492
+ // MB: handles first 'cleanupThread ' message, as thread is detached, so
493
+ // returnWorkerToPool() is called and all thread related structs are freed/released.
494
+ // MB: handles second 'cleanupThread' message which calls this function.
495
+ var alive = pthread && Atomics . exchange ( HEAPU32 , ( pthread_ptr + { { { C_STRUCTS . pthread . self } } } ) >> 2 , 0 ) ;
496
+ if ( alive ) {
514
497
var worker = pthread . worker ;
515
- PThread . returnWorkerToPool ( worker ) ;
498
+ PThread . returnWorkerToPool ( worker , terminate ) ;
516
499
}
517
500
} ,
518
501
@@ -768,9 +751,9 @@ var LibraryPThread = {
768
751
// magic ID to detect whether the pthread_t structure is 'alive'.
769
752
{ { { makeSetValue ( 'threadInfoStruct' , C_STRUCTS . pthread . self , 'threadInfoStruct' , 'i32' ) } } } ;
770
753
771
- // pthread struct prev and next should point to itself - this is used by
772
- // pthread_key_delete for deleting thread-specific data.
773
- { { { makeSetValue ( 'threadInfoStruct' , C_STRUCTS . pthread . prev , 0 , 'i32' ) } } } ;
754
+ // pthread struct prev and next should initially point to itself (see __init_tp in musl),
755
+ // this is used by pthread_key_delete for deleting thread-specific data.
756
+ { { { makeSetValue ( 'threadInfoStruct' , C_STRUCTS . pthread . prev , 'threadInfoStruct' , 'i32' ) } } } ;
774
757
{ { { makeSetValue ( 'threadInfoStruct' , C_STRUCTS . pthread . next , 'threadInfoStruct' , 'i32' ) } } } ;
775
758
776
759
// pthread struct robust_list head should point to itself.
@@ -843,7 +826,7 @@ var LibraryPThread = {
843
826
} ,
844
827
#endif
845
828
846
- pthread_kill__deps : [ '$killThread ', '$cancelThread ', 'emscripten_main_browser_thread_id '] ,
829
+ pthread_kill__deps : [ '$cancelThread ', '$cleanupThread ', 'emscripten_main_browser_thread_id '] ,
847
830
pthread_kill : function ( thread , signal ) {
848
831
if ( signal < 0 || signal >= 65 /*_NSIG*/ ) return ERRNO_CODES . EINVAL ;
849
832
if ( thread === _emscripten_main_browser_thread_id ( ) ) {
@@ -864,8 +847,8 @@ var LibraryPThread = {
864
847
if ( ! ENVIRONMENT_IS_PTHREAD ) cancelThread ( thread ) ;
865
848
else postMessage ( { 'cmd ': 'cancelThread ', 'thread ': thread } ) ;
866
849
} else if ( signal != 0 ) {
867
- if ( ! ENVIRONMENT_IS_PTHREAD ) killThread ( thread ) ;
868
- else postMessage ( { 'cmd ': 'killThread ', 'thread ': thread } ) ;
850
+ if ( ! ENVIRONMENT_IS_PTHREAD ) cleanupThread ( thread , true ) ;
851
+ else postMessage ( { 'cmd ': 'cleanupThread ', 'thread ': thread , ' terminate ': true } ) ;
869
852
}
870
853
return 0 ;
871
854
} ,
@@ -1085,11 +1068,11 @@ var LibraryPThread = {
1085
1068
} ,
1086
1069
1087
1070
emscripten_cleanup_thread__asm : true ,
1088
- emscripten_cleanup_thread__sig : 'vi ',
1071
+ emscripten_cleanup_thread__sig : 'vii ',
1089
1072
emscripten_cleanup_thread__deps : [ '$cleanupThread' ] ,
1090
- emscripten_cleanup_thread : function ( thread ) {
1091
- if ( ! ENVIRONMENT_IS_PTHREAD ) cleanupThread ( thread ) ;
1092
- else postMessage ( { 'cmd' : 'cleanupThread' , 'thread' : thread } ) ;
1073
+ emscripten_cleanup_thread : function ( thread , terminate ) {
1074
+ if ( ! ENVIRONMENT_IS_PTHREAD ) cleanupThread ( thread , terminate ) ;
1075
+ else postMessage ( { 'cmd' : 'cleanupThread' , 'thread' : thread , 'terminate' : terminate } ) ;
1093
1076
} ,
1094
1077
1095
1078
emscripten_conditional_set_current_thread_status_js : function ( expectedStatus , newStatus ) {
0 commit comments