@@ -31,6 +31,9 @@ var LibraryPThread = {
3131 $PThread__deps : [ '_emscripten_thread_init' ,
3232 '$killThread' ,
3333 '$cancelThread' , '$cleanupThread' , '$zeroMemory' ,
34+ #if MAIN_MODULE
35+ '$markAsZombie' ,
36+ #endif
3437 '$spawnThread' ,
3538 '_emscripten_thread_free_data' ,
3639 'exit' ,
@@ -55,6 +58,12 @@ var LibraryPThread = {
5558 // the reverse mapping, each worker has a `pthread_ptr` when its running a
5659 // pthread.
5760 pthreads : { } ,
61+ #if MAIN_MODULE
62+ // Zombie threads are threads that have finished running but we not yet
63+ // joined.
64+ zombieThreads : { } ,
65+ outstandingPromises : { } ,
66+ #endif
5867#if PTHREADS_DEBUG
5968 nextWorkerID : 1 ,
6069 debugInit : function ( ) {
@@ -267,6 +276,10 @@ var LibraryPThread = {
267276 spawnThread ( d ) ;
268277 } else if ( cmd === 'cleanupThread' ) {
269278 cleanupThread ( d [ 'thread' ] ) ;
279+ #if MAIN_MODULE
280+ } else if ( cmd === 'markAsZombie' ) {
281+ markAsZombie ( d [ 'thread' ] ) ;
282+ #endif
270283 } else if ( cmd === 'killThread' ) {
271284 killThread ( d [ 'thread' ] ) ;
272285 } else if ( cmd === 'cancelThread' ) {
@@ -538,11 +551,20 @@ var LibraryPThread = {
538551 } ,
539552
540553 $cleanupThread : function ( pthread_ptr ) {
554+ #if PTHREADS_DEBUG
555+ dbg ( 'cleanupThread: ' + ptrToString ( pthread_ptr ) )
556+ #endif
541557#if ASSERTIONS
542558 assert ( ! ENVIRONMENT_IS_PTHREAD , 'Internal Error! cleanupThread() can only ever be called from main application thread!' ) ;
543559 assert ( pthread_ptr , 'Internal Error! Null pthread_ptr in cleanupThread!' ) ;
544560#endif
545561 var worker = PThread . pthreads [ pthread_ptr ] ;
562+ #if MAIN_MODULE
563+ delete PThread . zombieThreads [ pthread_ptr ] ;
564+ if ( pthread_ptr in PThread . outstandingPromises ) {
565+ PThread . outstandingPromises [ pthread_ptr ] . resolve ( ) ;
566+ }
567+ #endif
546568 assert ( worker ) ;
547569 PThread . returnWorkerToPool ( worker ) ;
548570 } ,
@@ -1044,7 +1066,7 @@ var LibraryPThread = {
10441066 // Before we call the thread entry point, make sure any shared libraries
10451067 // have been loaded on this there. Otherwise our table migth be not be
10461068 // in sync and might not contain the function pointer `ptr` at all.
1047- __emscripten_thread_sync_code ( ) ;
1069+ __emscripten_dlsync_self ( ) ;
10481070#endif
10491071 // pthread entry points are always of signature 'void *ThreadMain(void *arg)'
10501072 // Native codebases sometimes spawn threads with other thread entry point
@@ -1073,6 +1095,90 @@ var LibraryPThread = {
10731095#endif
10741096 } ,
10751097
1098+ #if MAIN_MODULE
1099+ _emscripten_thread_exit_joinable : function ( thread ) {
1100+ // Called when a thread exits and is joinable. This puts the thread
1101+ // into zombie state where it can't run anymore work but cannot yet
1102+ // be cleaned up.
1103+ if ( ! ENVIRONMENT_IS_PTHREAD ) markAsZombie ( thread ) ;
1104+ else postMessage ( { 'cmd' : 'markAsZombie' , 'thread' : thread } ) ;
1105+ } ,
1106+
1107+ $markAsZombie : function ( pthread_ptr ) {
1108+ #if PTHREADS_DEBUG
1109+ dbg ( 'markAsZombie: ' + ptrToString ( pthread_ptr ) ) ;
1110+ #endif
1111+ PThread . zombieThreads [ pthread_ptr ] = true ;
1112+ if ( pthread_ptr in PThread . outstandingPromises ) {
1113+ PThread . outstandingPromises [ pthread_ptr ] . resolve ( ) ;
1114+ }
1115+ } ,
1116+
1117+ // Asynchronous version dlsync_threads. Always run on the main thread.
1118+ // This work happens asynchronously. The `callback` is called once this work
1119+ // is completed, passing the ctx.
1120+ // TODO(sbc): Should we make a new form of __proxy attribute for JS library
1121+ // function that run asynchronously like but blocks the caller until they are
1122+ // done. Perhaps "sync_with_ctx"?
1123+ _emscripten_dlsync_threads_async__sig : 'viii ',
1124+ _emscripten_dlsync_threads_async__deps : [ '_emscripten_proxy_dlsync_async' , '$newNativePromise' ] ,
1125+ _emscripten_dlsync_threads_async : function ( caller , callback , ctx ) {
1126+ #if PTHREADS_DEBUG
1127+ dbg ( "_emscripten_dlsync_threads_async caller=" + ptrToString ( caller ) ) ;
1128+ #endif
1129+ #if ASSERTIONS
1130+ assert ( ! ENVIRONMENT_IS_PTHREAD , 'Internal Error! _emscripten_dlsync_threads_async() can only ever be called from main thread' ) ;
1131+ #endif
1132+
1133+ const promises = [ ] ;
1134+ assert ( Object . keys ( PThread . outstandingPromises ) . length === 0 ) ;
1135+
1136+ // This first promise resolves once the main thread has loaded all modules.
1137+ promises . push ( newNativePromise ( __emscripten_dlsync_self_async , null ) . promise ) ;
1138+
1139+ // We then create a sequence of promises, one per thread, that resolve once
1140+ // each thread has performed its sync using _emscripten_proxy_dlsync.
1141+ // Any new threads that are created after this call will automaticaly be
1142+ // in sync because we call `__emscripten_dlsync_self` in
1143+ // invokeEntryPoint before the threads entry point is called.
1144+ for ( const ptr of Object . keys ( PThread . pthreads ) ) {
1145+ const pthread_ptr = Number ( ptr ) ;
1146+ if ( pthread_ptr !== caller && ! ( pthread_ptr in PThread . zombieThreads ) ) {
1147+ var p = newNativePromise ( __emscripten_proxy_dlsync_async , pthread_ptr ) ;
1148+ PThread . outstandingPromises [ pthread_ptr ] = p ;
1149+ promises . push ( p . promise ) ;
1150+ }
1151+ }
1152+
1153+ #if PTHREADS_DEBUG
1154+ dbg ( '_emscripten_dlsync_threads_async: waiting on ' + promises . length + ' promises' ) ;
1155+ #endif
1156+ // Once all promises are resolved then we know all threads are in sync and
1157+ // we can call the callback.
1158+ Promise . all ( promises ) . then ( ( ) => {
1159+ PThread . outstandingPromises = { } ;
1160+ #if PTHREADS_DEBUG
1161+ dbg ( '_emscripten_dlsync_threads_async done: calling callback' ) ;
1162+ #endif
1163+ { { { makeDynCall ( 'vp' , 'callback' ) } } } ( ctx ) ;
1164+ } ) ;
1165+ } ,
1166+
1167+ // Synchronous version dlsync_threads. Always run on the main thread.
1168+ _emscripten_dlsync_threads__deps : [ '_emscripten_proxy_dlsync' ] ,
1169+ _emscripten_dlsync_threads : function ( ) {
1170+ #if ASSERTIONS
1171+ assert ( ! ENVIRONMENT_IS_PTHREAD , 'Internal Error! _emscripten_dlsync_threads() can only ever be called from main thread' ) ;
1172+ #endif
1173+ for ( const ptr of Object . keys ( PThread . pthreads ) ) {
1174+ const pthread_ptr = Number ( ptr ) ;
1175+ if ( ! ( pthread_ptr in PThread . zombieThreads ) ) {
1176+ __emscripten_proxy_dlsync ( pthread_ptr ) ;
1177+ }
1178+ }
1179+ } ,
1180+ #endif // MAIN_MODULE
1181+
10761182 $executeNotifiedProxyingQueue : function ( queue ) {
10771183 // Set the notification state to processing.
10781184 Atomics . store ( HEAP32 , queue >> 2 , { { { cDefine ( 'NOTIFICATION_RECEIVED' ) } } } ) ;
0 commit comments