@@ -31,6 +31,9 @@ var LibraryPThread = {
3131 $PThread__deps : [ '_emscripten_thread_init' ,
3232 '$killThread' ,
3333 '$cancelThread' , '$cleanupThread' , '$zeroMemory' ,
34+ #if MAIN_MODULE
35+ '$markAsFinshed' ,
36+ #endif
3437 '$spawnThread' ,
3538 '_emscripten_thread_free_data' ,
3639 'exit' ,
@@ -97,6 +100,12 @@ var LibraryPThread = {
97100 while ( pthreadPoolSize -- ) {
98101 PThread . allocateUnusedWorker ( ) ;
99102 }
103+ #endif
104+ #if MAIN_MODULE
105+ PThread . outstandingPromises = { } ;
106+ // Finished threads are threads that have finished running but we not yet
107+ // joined.
108+ PThread . finishedThreads = new Set ( ) ;
100109#endif
101110 } ,
102111
@@ -269,6 +278,10 @@ var LibraryPThread = {
269278 spawnThread ( d ) ;
270279 } else if ( cmd === 'cleanupThread' ) {
271280 cleanupThread ( d [ 'thread' ] ) ;
281+ #if MAIN_MODULE
282+ } else if ( cmd === 'markAsFinshed' ) {
283+ markAsFinshed ( d [ 'thread' ] ) ;
284+ #endif
272285 } else if ( cmd === 'killThread' ) {
273286 killThread ( d [ 'thread' ] ) ;
274287 } else if ( cmd === 'cancelThread' ) {
@@ -540,11 +553,20 @@ var LibraryPThread = {
540553 } ,
541554
542555 $cleanupThread : function ( pthread_ptr ) {
556+ #if PTHREADS_DEBUG
557+ dbg ( 'cleanupThread: ' + ptrToString ( pthread_ptr ) )
558+ #endif
543559#if ASSERTIONS
544560 assert ( ! ENVIRONMENT_IS_PTHREAD , 'Internal Error! cleanupThread() can only ever be called from main application thread!' ) ;
545561 assert ( pthread_ptr , 'Internal Error! Null pthread_ptr in cleanupThread!' ) ;
546562#endif
547563 var worker = PThread . pthreads [ pthread_ptr ] ;
564+ #if MAIN_MODULE
565+ PThread . finishedThreads . delete ( pthread_ptr ) ;
566+ if ( pthread_ptr in PThread . outstandingPromises ) {
567+ PThread . outstandingPromises [ pthread_ptr ] . resolve ( ) ;
568+ }
569+ #endif
548570 assert ( worker ) ;
549571 PThread . returnWorkerToPool ( worker ) ;
550572 } ,
@@ -1046,7 +1068,7 @@ var LibraryPThread = {
10461068 // Before we call the thread entry point, make sure any shared libraries
10471069 // have been loaded on this there. Otherwise our table migth be not be
10481070 // in sync and might not contain the function pointer `ptr` at all.
1049- __emscripten_thread_sync_code ( ) ;
1071+ __emscripten_dlsync_self ( ) ;
10501072#endif
10511073 // pthread entry points are always of signature 'void *ThreadMain(void *arg)'
10521074 // Native codebases sometimes spawn threads with other thread entry point
@@ -1075,6 +1097,95 @@ var LibraryPThread = {
10751097#endif
10761098 } ,
10771099
1100+ #if MAIN_MODULE
1101+ _emscripten_thread_exit_joinable : function ( thread ) {
1102+ // Called when a thread exits and is joinable. We mark these threads
1103+ // as finished, which means that are in state where are no longer actually
1104+ // runnning, but remain around waiting to be joined. In this state they
1105+ // cannot run any more proxied work.
1106+ if ( ! ENVIRONMENT_IS_PTHREAD ) markAsFinshed ( thread ) ;
1107+ else postMessage ( { 'cmd' : 'markAsFinshed' , 'thread' : thread } ) ;
1108+ } ,
1109+
1110+ $markAsFinshed : function ( pthread_ptr ) {
1111+ #if PTHREADS_DEBUG
1112+ dbg ( 'markAsFinshed: ' + ptrToString ( pthread_ptr ) ) ;
1113+ #endif
1114+ PThread . finishedThreads . add ( pthread_ptr ) ;
1115+ if ( pthread_ptr in PThread . outstandingPromises ) {
1116+ PThread . outstandingPromises [ pthread_ptr ] . resolve ( ) ;
1117+ }
1118+ } ,
1119+
1120+ // Asynchronous version dlsync_threads. Always run on the main thread.
1121+ // This work happens asynchronously. The `callback` is called once this work
1122+ // is completed, passing the ctx.
1123+ // TODO(sbc): Should we make a new form of __proxy attribute for JS library
1124+ // function that run asynchronously like but blocks the caller until they are
1125+ // done. Perhaps "sync_with_ctx"?
1126+ _emscripten_dlsync_threads_async__sig : 'viii ',
1127+ _emscripten_dlsync_threads_async__deps : [ '_emscripten_proxy_dlsync_async' , '$newNativePromise' ] ,
1128+ _emscripten_dlsync_threads_async : function ( caller , callback , ctx ) {
1129+ #if PTHREADS_DEBUG
1130+ dbg ( "_emscripten_dlsync_threads_async caller=" + ptrToString ( caller ) ) ;
1131+ #endif
1132+ #if ASSERTIONS
1133+ assert ( ! ENVIRONMENT_IS_PTHREAD , 'Internal Error! _emscripten_dlsync_threads_async() can only ever be called from main thread' ) ;
1134+ #endif
1135+
1136+ const promises = [ ] ;
1137+ assert ( Object . keys ( PThread . outstandingPromises ) . length === 0 ) ;
1138+
1139+ // This first promise resolves once the main thread has loaded all modules.
1140+ promises . push ( newNativePromise ( __emscripten_dlsync_self_async , null ) . promise ) ;
1141+
1142+ // We then create a sequence of promises, one per thread, that resolve once
1143+ // each thread has performed its sync using _emscripten_proxy_dlsync.
1144+ // Any new threads that are created after this call will automaticaly be
1145+ // in sync because we call `__emscripten_dlsync_self` in
1146+ // invokeEntryPoint before the threads entry point is called.
1147+ for ( const ptr of Object . keys ( PThread . pthreads ) ) {
1148+ const pthread_ptr = Number ( ptr ) ;
1149+ if ( pthread_ptr !== caller && ! PThread . finishedThreads . has ( pthread_ptr ) ) {
1150+ var p = newNativePromise ( __emscripten_proxy_dlsync_async , pthread_ptr ) ;
1151+ PThread . outstandingPromises [ pthread_ptr ] = p ;
1152+ promises . push ( p . promise ) ;
1153+ }
1154+ }
1155+
1156+ #if PTHREADS_DEBUG
1157+ dbg ( '_emscripten_dlsync_threads_async: waiting on ' + promises . length + ' promises' ) ;
1158+ #endif
1159+ // Once all promises are resolved then we know all threads are in sync and
1160+ // we can call the callback.
1161+ Promise . all ( promises ) . then ( ( ) => {
1162+ PThread . outstandingPromises = { } ;
1163+ #if PTHREADS_DEBUG
1164+ dbg ( '_emscripten_dlsync_threads_async done: calling callback' ) ;
1165+ #endif
1166+ { { { makeDynCall ( 'vp' , 'callback' ) } } } ( ctx ) ;
1167+ } ) ;
1168+ } ,
1169+
1170+ // Synchronous version dlsync_threads. This is only needed for the case then
1171+ // the main thread call dlopen and in that case we have not choice but to
1172+ // synchronously block the main thread until all other threads are in sync.
1173+ // When `dlopen` is called from a worker, the worker itself is blocked but
1174+ // the operation its waiting on (on the main thread) can be async.
1175+ _emscripten_dlsync_threads__deps : [ '_emscripten_proxy_dlsync' ] ,
1176+ _emscripten_dlsync_threads : function ( ) {
1177+ #if ASSERTIONS
1178+ assert ( ! ENVIRONMENT_IS_PTHREAD , 'Internal Error! _emscripten_dlsync_threads() can only ever be called from main thread' ) ;
1179+ #endif
1180+ for ( const ptr of Object . keys ( PThread . pthreads ) ) {
1181+ const pthread_ptr = Number ( ptr ) ;
1182+ if ( ! PThread . finishedThreads . has ( pthread_ptr ) ) {
1183+ __emscripten_proxy_dlsync ( pthread_ptr ) ;
1184+ }
1185+ }
1186+ } ,
1187+ #endif // MAIN_MODULE
1188+
10781189 $executeNotifiedProxyingQueue : function ( queue ) {
10791190 // Set the notification state to processing.
10801191 Atomics . store ( HEAP32 , queue >> 2 , { { { cDefine ( 'NOTIFICATION_RECEIVED' ) } } } ) ;
0 commit comments