Skip to content

Commit 7551ae8

Browse files
committed
Block in dlopen until all threads have loaded the module
Fixes: #18345
1 parent 82c9cd7 commit 7551ae8

File tree

12 files changed

+298
-106
lines changed

12 files changed

+298
-106
lines changed

emcc.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1634,7 +1634,12 @@ def setup_pthreads(target):
16341634
]
16351635

16361636
if settings.MAIN_MODULE:
1637-
settings.REQUIRED_EXPORTS += ['_emscripten_thread_sync_code', '__dl_seterr']
1637+
settings.REQUIRED_EXPORTS += [
1638+
'_emscripten_thread_sync_code',
1639+
'_emscripten_thread_sync_code_async',
1640+
'_emscripten_proxy_sync_code',
1641+
'__dl_seterr',
1642+
]
16381643

16391644
settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += [
16401645
'$exitOnMainThread',

src/library_pthread.js

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1069,6 +1069,83 @@ var LibraryPThread = {
10691069
#endif
10701070
},
10711071

1072+
#if MAIN_MODULE
1073+
$promiseMap: "new Map();",
1074+
$nextPromiseId: 1,
1075+
1076+
// Create a new promise that can be resolved or rejected by passing
1077+
// a unique ID to emscripten_promise_resolve/emscripten_promise_reject
1078+
$newNativePromise__deps: ['$promiseMap', '$nextPromiseId'],
1079+
$newNativePromise: function(func, args) {
1080+
return new Promise((resolve, reject) => {
1081+
var promiseId = nextPromiseId;
1082+
nextPromiseId += 1;
1083+
promiseMap.set(promiseId, {resolve, reject});
1084+
// Native promise function take promise ID as last argument
1085+
args.push(promiseId);
1086+
func.apply(null, args);
1087+
});
1088+
},
1089+
1090+
emscripten_promise_resolve__deps: ['$promiseMap'],
1091+
emscripten_promise_resolve__sig: 'vip',
1092+
emscripten_promise_resolve: function(id, value) {
1093+
#if RUNTIME_DEBUG
1094+
err('emscripten_resolve_promise: ' + id);
1095+
#endif
1096+
assert(promiseMap.has(id));
1097+
promiseMap.get(id).resolve(value);
1098+
promiseMap.delete(id);
1099+
},
1100+
1101+
emscripten_promise_reject__deps: ['$promiseMap'],
1102+
emscripten_promise_reject__sig: 'vip',
1103+
emscripten_promise_reject: function(id) {
1104+
#if RUNTIME_DEBUG
1105+
dbg('emscripten_promise_reject: ' + id);
1106+
#endif
1107+
assert(promiseMap.has(id));
1108+
promiseMap.get(id).reject();
1109+
},
1110+
1111+
// Called on the main thread to syncronize the code loaded on all threads.
1112+
// This work happens asyncronously. The `callback` is called once this work
1113+
// is completely, passing the ctx.
1114+
_emscripten_sync_all_threads__sig: 'viii',
1115+
_emscripten_sync_all_threads__deps: ['_emscripten_proxy_sync_code', '$newNativePromise'],
1116+
_emscripten_sync_all_threads: function(caller, callback, ctx) {
1117+
#if PTHREADS_DEBUG
1118+
dbg("_emscripten_sync_all_threads caller=" + ptrToString(caller));
1119+
#endif
1120+
#if ASSERTIONS
1121+
assert(!ENVIRONMENT_IS_PTHREAD, 'Internal Error! _emscripten_sync_all_threads() can only ever be called from main thread');
1122+
#endif
1123+
1124+
let promises = [];
1125+
1126+
// This first promise resolves once the main thread has loaded all module
1127+
promises.push(newNativePromise(__emscripten_thread_sync_code_async, [0]));
1128+
1129+
// We then create a sequence of promises, one per thread, that resolve once
1130+
// each thread has performed its sync using _emscripten_proxy_sync_code.
1131+
for (const ptr of Object.keys(PThread.pthreads)) {
1132+
const pthread_ptr = Number(ptr);
1133+
if (pthread_ptr !== caller) {
1134+
promises.push(newNativePromise(__emscripten_proxy_sync_code, [pthread_ptr]));
1135+
}
1136+
}
1137+
1138+
// Once all promises are resolved then we we know all threads are in
1139+
// sync and we can call the callback.
1140+
Promise.all(promises).then(() => {
1141+
#if PTHREADS_DEBUG
1142+
dbg("_emscripten_sync_all_threads done: calling callback");
1143+
#endif
1144+
{{{ makeDynCall('vp', 'callback') }}}(ctx);
1145+
});
1146+
},
1147+
#endif
1148+
10721149
$executeNotifiedProxyingQueue: function(queue) {
10731150
// Set the notification state to processing.
10741151
Atomics.store(HEAP32, queue >> 2, {{{ cDefine('NOTIFICATION_RECEIVED') }}});

system/include/emscripten/threading.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -286,9 +286,6 @@ int emscripten_pthread_attr_settransferredcanvases(pthread_attr_t *a, const char
286286
// blocking is not enabled, see ALLOW_BLOCKING_ON_MAIN_THREAD.
287287
void emscripten_check_blocking_allowed(void);
288288

289-
// Experimental API for syncing loaded code between pthreads.
290-
void _emscripten_thread_sync_code();
291-
292289
void _emscripten_yield();
293290

294291
#ifdef __cplusplus

0 commit comments

Comments
 (0)