@@ -31,6 +31,9 @@ var LibraryPThread = {
31
31
$PThread__deps : [ '_emscripten_thread_init' ,
32
32
'$killThread' ,
33
33
'$cancelThread' , '$cleanupThread' , '$zeroMemory' ,
34
+ #if MAIN_MODULE
35
+ '$markAsFinshed' ,
36
+ #endif
34
37
'$spawnThread' ,
35
38
'_emscripten_thread_free_data' ,
36
39
'exit' ,
@@ -101,6 +104,12 @@ var LibraryPThread = {
101
104
while ( pthreadPoolSize -- ) {
102
105
PThread . allocateUnusedWorker ( ) ;
103
106
}
107
+ #endif
108
+ #if MAIN_MODULE
109
+ PThread . outstandingPromises = { } ;
110
+ // Finished threads are threads that have finished running but we not yet
111
+ // joined.
112
+ PThread . finishedThreads = new Set ( ) ;
104
113
#endif
105
114
} ,
106
115
@@ -273,6 +282,10 @@ var LibraryPThread = {
273
282
spawnThread ( d ) ;
274
283
} else if ( cmd === 'cleanupThread' ) {
275
284
cleanupThread ( d [ 'thread' ] ) ;
285
+ #if MAIN_MODULE
286
+ } else if ( cmd === 'markAsFinshed' ) {
287
+ markAsFinshed ( d [ 'thread' ] ) ;
288
+ #endif
276
289
} else if ( cmd === 'killThread' ) {
277
290
killThread ( d [ 'thread' ] ) ;
278
291
} else if ( cmd === 'cancelThread' ) {
@@ -544,11 +557,20 @@ var LibraryPThread = {
544
557
} ,
545
558
546
559
$cleanupThread : function ( pthread_ptr ) {
560
+ #if PTHREADS_DEBUG
561
+ dbg ( 'cleanupThread: ' + ptrToString ( pthread_ptr ) )
562
+ #endif
547
563
#if ASSERTIONS
548
564
assert ( ! ENVIRONMENT_IS_PTHREAD , 'Internal Error! cleanupThread() can only ever be called from main application thread!' ) ;
549
565
assert ( pthread_ptr , 'Internal Error! Null pthread_ptr in cleanupThread!' ) ;
550
566
#endif
551
567
var worker = PThread . pthreads [ pthread_ptr ] ;
568
+ #if MAIN_MODULE
569
+ PThread . finishedThreads . delete ( pthread_ptr ) ;
570
+ if ( pthread_ptr in PThread . outstandingPromises ) {
571
+ PThread . outstandingPromises [ pthread_ptr ] . resolve ( ) ;
572
+ }
573
+ #endif
552
574
assert ( worker ) ;
553
575
PThread . returnWorkerToPool ( worker ) ;
554
576
} ,
@@ -1052,7 +1074,7 @@ var LibraryPThread = {
1052
1074
// Before we call the thread entry point, make sure any shared libraries
1053
1075
// have been loaded on this there. Otherwise our table migth be not be
1054
1076
// in sync and might not contain the function pointer `ptr` at all.
1055
- __emscripten_thread_sync_code ( ) ;
1077
+ __emscripten_dlsync_self ( ) ;
1056
1078
#endif
1057
1079
// pthread entry points are always of signature 'void *ThreadMain(void *arg)'
1058
1080
// Native codebases sometimes spawn threads with other thread entry point
@@ -1081,6 +1103,99 @@ var LibraryPThread = {
1081
1103
#endif
1082
1104
} ,
1083
1105
1106
+ #if MAIN_MODULE
1107
+ _emscripten_thread_exit_joinable : function ( thread ) {
1108
+ // Called when a thread exits and is joinable. We mark these threads
1109
+ // as finished, which means that are in state where are no longer actually
1110
+ // runnning, but remain around waiting to be joined. In this state they
1111
+ // cannot run any more proxied work.
1112
+ if ( ! ENVIRONMENT_IS_PTHREAD ) markAsFinshed ( thread ) ;
1113
+ else postMessage ( { 'cmd' : 'markAsFinshed' , 'thread' : thread } ) ;
1114
+ } ,
1115
+
1116
+ $markAsFinshed : function ( pthread_ptr ) {
1117
+ #if PTHREADS_DEBUG
1118
+ dbg ( 'markAsFinshed: ' + ptrToString ( pthread_ptr ) ) ;
1119
+ #endif
1120
+ PThread . finishedThreads . add ( pthread_ptr ) ;
1121
+ if ( pthread_ptr in PThread . outstandingPromises ) {
1122
+ PThread . outstandingPromises [ pthread_ptr ] . resolve ( ) ;
1123
+ }
1124
+ } ,
1125
+
1126
+ // Asynchronous version dlsync_threads. Always run on the main thread.
1127
+ // This work happens asynchronously. The `callback` is called once this work
1128
+ // is completed, passing the ctx.
1129
+ // TODO(sbc): Should we make a new form of __proxy attribute for JS library
1130
+ // function that run asynchronously like but blocks the caller until they are
1131
+ // done. Perhaps "sync_with_ctx"?
1132
+ _emscripten_dlsync_threads_async__sig : 'vppp ',
1133
+ _emscripten_dlsync_threads_async__deps : [ '_emscripten_proxy_dlsync_async' , 'emscripten_promise_create' , '$getPromise' ] ,
1134
+ _emscripten_dlsync_threads_async : function ( caller , callback , ctx ) {
1135
+ #if PTHREADS_DEBUG
1136
+ dbg ( "_emscripten_dlsync_threads_async caller=" + ptrToString ( caller ) ) ;
1137
+ #endif
1138
+ #if ASSERTIONS
1139
+ assert ( ! ENVIRONMENT_IS_PTHREAD , 'Internal Error! _emscripten_dlsync_threads_async() can only ever be called from main thread' ) ;
1140
+ #endif
1141
+
1142
+ const promises = [ ] ;
1143
+ assert ( Object . keys ( PThread . outstandingPromises ) . length === 0 ) ;
1144
+
1145
+ // This first promise resolves once the main thread has loaded all modules.
1146
+ var info = makePromise ( ) ;
1147
+ promises . push ( info . promise ) ;
1148
+ __emscripten_dlsync_self_async ( info . id ) ;
1149
+
1150
+
1151
+ // We then create a sequence of promises, one per thread, that resolve once
1152
+ // each thread has performed its sync using _emscripten_proxy_dlsync.
1153
+ // Any new threads that are created after this call will automaticaly be
1154
+ // in sync because we call `__emscripten_dlsync_self` in
1155
+ // invokeEntryPoint before the threads entry point is called.
1156
+ for ( const ptr of Object . keys ( PThread . pthreads ) ) {
1157
+ const pthread_ptr = Number ( ptr ) ;
1158
+ if ( pthread_ptr !== caller && ! PThread . finishedThreads . has ( pthread_ptr ) ) {
1159
+ info = makePromise ( ) ;
1160
+ __emscripten_proxy_dlsync_async ( pthread_ptr , info . id ) ;
1161
+ PThread . outstandingPromises [ pthread_ptr ] = info ;
1162
+ promises . push ( info . promise ) ;
1163
+ }
1164
+ }
1165
+
1166
+ #if PTHREADS_DEBUG
1167
+ dbg ( '_emscripten_dlsync_threads_async: waiting on ' + promises . length + ' promises' ) ;
1168
+ #endif
1169
+ // Once all promises are resolved then we know all threads are in sync and
1170
+ // we can call the callback.
1171
+ Promise . all ( promises ) . then ( ( ) => {
1172
+ PThread . outstandingPromises = { } ;
1173
+ #if PTHREADS_DEBUG
1174
+ dbg ( '_emscripten_dlsync_threads_async done: calling callback' ) ;
1175
+ #endif
1176
+ { { { makeDynCall ( 'vp' , 'callback' ) } } } ( ctx ) ;
1177
+ } ) ;
1178
+ } ,
1179
+
1180
+ // Synchronous version dlsync_threads. This is only needed for the case then
1181
+ // the main thread call dlopen and in that case we have not choice but to
1182
+ // synchronously block the main thread until all other threads are in sync.
1183
+ // When `dlopen` is called from a worker, the worker itself is blocked but
1184
+ // the operation its waiting on (on the main thread) can be async.
1185
+ _emscripten_dlsync_threads__deps : [ '_emscripten_proxy_dlsync' ] ,
1186
+ _emscripten_dlsync_threads : function ( ) {
1187
+ #if ASSERTIONS
1188
+ assert ( ! ENVIRONMENT_IS_PTHREAD , 'Internal Error! _emscripten_dlsync_threads() can only ever be called from main thread' ) ;
1189
+ #endif
1190
+ for ( const ptr of Object . keys ( PThread . pthreads ) ) {
1191
+ const pthread_ptr = Number ( ptr ) ;
1192
+ if ( ! PThread . finishedThreads . has ( pthread_ptr ) ) {
1193
+ __emscripten_proxy_dlsync ( pthread_ptr ) ;
1194
+ }
1195
+ }
1196
+ } ,
1197
+ #endif // MAIN_MODULE
1198
+
1084
1199
$executeNotifiedProxyingQueue : function ( queue ) {
1085
1200
// Set the notification state to processing.
1086
1201
Atomics . store ( HEAP32 , queue >> 2 , { { { cDefine ( 'NOTIFICATION_RECEIVED' ) } } } ) ;
0 commit comments