@@ -8,7 +8,7 @@ var LibraryPThread = {
88 $PThread__postset : 'if (!ENVIRONMENT_IS_PTHREAD) PThread.initMainThreadBlock();' ,
99 $PThread__deps : [ '_emscripten_thread_init' ,
1010 'emscripten_register_main_browser_thread_id' ,
11- '$ERRNO_CODES' , 'emscripten_futex_wake' , '$killThread' ,
11+ '$ERRNO_CODES' , 'emscripten_futex_wake' ,
1212 '$cancelThread' , '$cleanupThread'
1313#if USE_ASAN || USE_LSAN
1414 , '$withBuiltinMalloc'
@@ -191,7 +191,7 @@ var LibraryPThread = {
191191 Atomics . store ( HEAPU32 , ( tb + { { { C_STRUCTS . pthread . detach_state } } } ) >> 2 , { { { cDefine ( 'DT_EXITED' ) } } } ) ; // Mark the thread as no longer running.
192192
193193 // 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 ) ;
195195
196196 // Not hosting a pthread anymore in this worker, reset the info structures to null.
197197 __emscripten_thread_init ( 0 , 0 , 0 ) ; // Unregister the thread block also inside the asm.js scope.
@@ -209,24 +209,24 @@ var LibraryPThread = {
209209 if ( ENVIRONMENT_IS_PTHREAD ) {
210210 // Note: in theory we would like to return any offscreen canvases back to the main thread,
211211 // 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 } ) ;
213213 }
214214 }
215215 } ,
216216
217+ terminateAllThreads__deps : [ '$cleanupThread' ] ,
217218 terminateAllThreads : function ( ) {
218219 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 ) ;
222222 }
223223 }
224224 PThread . pthreads = { } ;
225225
226226 for ( var i = 0 ; i < PThread . unusedWorkers . length ; ++ i ) {
227227 var worker = PThread . unusedWorkers [ i ] ;
228228#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' ) ;
230230#endif
231231 worker . terminate ( ) ;
232232 }
@@ -238,14 +238,14 @@ var LibraryPThread = {
238238#if ASSERTIONS
239239 assert ( pthread , 'This Worker should have a pthread it is executing' ) ;
240240#endif
241- PThread . freeThreadData ( pthread ) ;
242- worker . terminate ( ) ;
241+ cleanupThread ( pthread , true ) ;
243242 }
244243 PThread . runningWorkers = [ ] ;
245244 } ,
246245 freeThreadData : function ( pthread ) {
247246 if ( ! pthread ) return ;
248247 if ( pthread . threadInfoStruct ) {
248+ // Free thread-specific data
249249 var tlsMemory = { { { makeGetValue ( 'pthread.threadInfoStruct' , C_STRUCTS . pthread . tsd , 'i32' ) } } } ;
250250 { { { makeSetValue ( 'pthread.threadInfoStruct' , C_STRUCTS . pthread . tsd , 0 , 'i32' ) } } } ;
251251#if PTHREADS_PROFILING
@@ -261,19 +261,23 @@ var LibraryPThread = {
261261 pthread . stackBase = 0 ;
262262 if ( pthread . worker ) pthread . worker . pthread = null ;
263263 } ,
264- returnWorkerToPool: function ( worker ) {
264+ returnWorkerToPool: function ( worker , terminate ) {
265265 // We don't want to run main thread queued calls here, since we are doing
266266 // some operations that leave the worker queue in an invalid state until
267267 // we are completely done (it would be bad if free() ends up calling a
268268 // queued pthread_create which looks at the global data structures we are
269269 // modifying).
270270 PThread . runWithoutMainThreadQueuedCalls ( function ( ) {
271271 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+ }
274278 PThread . runningWorkers . splice ( PThread . runningWorkers . indexOf ( worker ) , 1 ) ; // Not a running Worker anymore
275279 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.
277281 worker . pthread = undefined ;
278282 } ) ;
279283 } ,
@@ -345,9 +349,7 @@ var LibraryPThread = {
345349 } else if ( cmd === 'spawnThread' ) {
346350 spawnThread ( e . data ) ;
347351 } else if ( cmd === 'cleanupThread' ) {
348- cleanupThread ( d [ 'thread' ] ) ;
349- } else if ( cmd === 'killThread' ) {
350- killThread ( d [ 'thread' ] ) ;
352+ cleanupThread ( d [ 'thread' ] , d [ 'terminate' ] ) ;
351353 } else if ( cmd === 'cancelThread' ) {
352354 cancelThread ( d [ 'thread' ] ) ;
353355 } else if ( cmd === 'loaded' ) {
@@ -364,12 +366,6 @@ var LibraryPThread = {
364366 err ( 'Thread ' + d [ 'threadId' ] + ': ' + d [ 'text' ] ) ;
365367 } else if ( cmd === 'alert' ) {
366368 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- }
373369 } else if ( cmd === 'exitProcess' ) {
374370 // A pthread has requested to exit the whole application process (runtime).
375371#if ASSERTIONS
@@ -478,41 +474,28 @@ var LibraryPThread = {
478474 }
479475 } ,
480476
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 ) {
495478 if ( ENVIRONMENT_IS_PTHREAD ) throw 'Internal Error! cleanupThread() can only ever be called from main application thread!' ;
496479 if ( ! pthread_ptr ) throw 'Internal Error! Null pthread_ptr in cleanupThread!' ;
497480 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:
500483 // 1. Joining cancelled thread - in such situation it may happen that pthread data will
501484 // already be removed by handling 'cancelDone' message.
502485 // 2. Joining thread from non-main browser thread (this also includes thread running main()
503486 // when compiled with `PROXY_TO_PTHREAD`) - in such situation it may happen that following
504487 // 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
506489 // S1: calls pthread_join(S2), this causes:
507490 // a. S2 is marked as detached,
508491 // 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 ) {
514497 var worker = pthread . worker ;
515- PThread . returnWorkerToPool ( worker ) ;
498+ PThread . returnWorkerToPool ( worker , terminate ) ;
516499 }
517500 } ,
518501
@@ -768,9 +751,9 @@ var LibraryPThread = {
768751 // magic ID to detect whether the pthread_t structure is 'alive'.
769752 { { { makeSetValue ( 'threadInfoStruct' , C_STRUCTS . pthread . self , 'threadInfoStruct' , 'i32' ) } } } ;
770753
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' ) } } } ;
774757 { { { makeSetValue ( 'threadInfoStruct' , C_STRUCTS . pthread . next , 'threadInfoStruct' , 'i32' ) } } } ;
775758
776759 // pthread struct robust_list head should point to itself.
@@ -839,7 +822,7 @@ var LibraryPThread = {
839822 emscripten_builtin_pthread_join : 'pthread_join ',
840823#endif
841824
842- pthread_kill__deps : [ '$killThread ', '$cancelThread ', 'emscripten_main_browser_thread_id '] ,
825+ pthread_kill__deps : [ '$cancelThread ', '$cleanupThread ', 'emscripten_main_browser_thread_id '] ,
843826 pthread_kill : function ( thread , signal ) {
844827 if ( signal < 0 || signal >= 65 /*_NSIG*/ ) return ERRNO_CODES . EINVAL ;
845828 if ( thread === _emscripten_main_browser_thread_id ( ) ) {
@@ -860,8 +843,8 @@ var LibraryPThread = {
860843 if ( ! ENVIRONMENT_IS_PTHREAD ) cancelThread ( thread ) ;
861844 else postMessage ( { 'cmd ': 'cancelThread ', 'thread ': thread } ) ;
862845 } else if ( signal != 0 ) {
863- if ( ! ENVIRONMENT_IS_PTHREAD ) killThread ( thread ) ;
864- else postMessage ( { 'cmd ': 'killThread ', 'thread ': thread } ) ;
846+ if ( ! ENVIRONMENT_IS_PTHREAD ) cleanupThread ( thread , true ) ;
847+ else postMessage ( { 'cmd ': 'cleanupThread ', 'thread ': thread , ' terminate ': true } ) ;
865848 }
866849 return 0 ;
867850 } ,
@@ -1081,11 +1064,11 @@ var LibraryPThread = {
10811064 } ,
10821065
10831066 emscripten_cleanup_thread__asm : true ,
1084- emscripten_cleanup_thread__sig : 'vi ',
1067+ emscripten_cleanup_thread__sig : 'vii ',
10851068 emscripten_cleanup_thread__deps : [ '$cleanupThread' ] ,
1086- emscripten_cleanup_thread : function ( thread ) {
1087- if ( ! ENVIRONMENT_IS_PTHREAD ) cleanupThread ( thread ) ;
1088- else postMessage ( { 'cmd' : 'cleanupThread' , 'thread' : thread } ) ;
1069+ emscripten_cleanup_thread : function ( thread , terminate ) {
1070+ if ( ! ENVIRONMENT_IS_PTHREAD ) cleanupThread ( thread , terminate ) ;
1071+ else postMessage ( { 'cmd' : 'cleanupThread' , 'thread' : thread , 'terminate' : terminate } ) ;
10891072 } ,
10901073
10911074 emscripten_conditional_set_current_thread_status_js : function ( expectedStatus , newStatus ) {
0 commit comments