diff --git a/src/lib/libeventloop.js b/src/lib/libeventloop.js index 79a24520cb17b..67a522d78692f 100644 --- a/src/lib/libeventloop.js +++ b/src/lib/libeventloop.js @@ -85,6 +85,15 @@ LibraryJSEventLoop = { $emClearImmediate_deps: ['$emSetImmediate'], $emClearImmediate: undefined, + emscripten_queue_microtask__deps: ['$emSetImmediate', '$callUserCallback'], + emscripten_queue_microtask: (cb, userData) => { + {{{ runtimeKeepalivePush(); }}} + return queueMicrotask(() => { + {{{ runtimeKeepalivePop(); }}} + callUserCallback(() => {{{ makeDynCall('vp', 'cb') }}}(userData)); + }); + }, + emscripten_set_immediate__deps: ['$emSetImmediate', '$callUserCallback'], emscripten_set_immediate: (cb, userData) => { {{{ runtimeKeepalivePush(); }}} diff --git a/src/lib/libpromise.js b/src/lib/libpromise.js index db4838e09cc22..9c9912b681410 100644 --- a/src/lib/libpromise.js +++ b/src/lib/libpromise.js @@ -51,7 +51,7 @@ addToLibrary({ 'emscripten_promise_destroy'], emscripten_promise_resolve: (id, result, value) => { #if RUNTIME_DEBUG - dbg(`emscripten_promise_resolve: ${id}`); + dbg(`emscripten_promise_resolve: ${id} -> ${value}`); #endif var info = promiseMap.get(id); switch (result) { @@ -59,9 +59,15 @@ addToLibrary({ info.resolve(value); return; case {{{ cDefs.EM_PROMISE_MATCH }}}: +#if ASSERTIONS + assert(id != value, 'cannot resolve promise to itself') +#endif info.resolve(getPromise(value)); return; case {{{ cDefs.EM_PROMISE_MATCH_RELEASE }}}: +#if ASSERTIONS + assert(id != value, 'cannot resolve promise to itself') +#endif info.resolve(getPromise(value)); _emscripten_promise_destroy(value); return; diff --git a/src/lib/libsigs.js b/src/lib/libsigs.js index 2e1b8546b3e41..5ee43b933413c 100644 --- a/src/lib/libsigs.js +++ b/src/lib/libsigs.js @@ -733,6 +733,7 @@ sigs = { emscripten_promise_race__sig: 'ppp', emscripten_promise_resolve__sig: 'vpip', emscripten_promise_then__sig: 'ppppp', + emscripten_queue_microtask__sig: 'ipp', emscripten_random__sig: 'f', emscripten_request_animation_frame__sig: 'ipp', emscripten_request_animation_frame_loop__sig: 'vpp', diff --git a/system/include/emscripten/eventloop.h b/system/include/emscripten/eventloop.h index 4f706f9acf63b..401f09de8e82f 100644 --- a/system/include/emscripten/eventloop.h +++ b/system/include/emscripten/eventloop.h @@ -26,6 +26,8 @@ void emscripten_set_immediate_loop(bool (*cb)(void *user_data), void *user_data) int emscripten_set_interval(void (*cb)(void *user_data) __attribute__((nonnull)), double interval_ms, void *user_data); void emscripten_clear_interval(int id); +int emscripten_queue_microtask(void (*cb)(void *user_data) __attribute__((nonnull)), void *user_data); + void emscripten_runtime_keepalive_push(void); void emscripten_runtime_keepalive_pop(void); bool emscripten_runtime_keepalive_check(void); diff --git a/system/lib/libc/dynlink.c b/system/lib/libc/dynlink.c index 71d9e14b98455..b8930167f932d 100644 --- a/system/lib/libc/dynlink.c +++ b/system/lib/libc/dynlink.c @@ -40,6 +40,7 @@ #endif struct async_data { + struct dso* dso; em_dlopen_callback onsuccess; em_arg_callback_func onerror; void* user_data; @@ -468,6 +469,7 @@ static void dlopen_onsuccess(struct dso* dso, void* user_data) { dso, dso->mem_addr, dso->mem_size); + assert(dso == data->dso); load_library_done(dso); do_write_unlock(); data->onsuccess(data->user_data, dso); @@ -601,6 +603,16 @@ void* dlopen(const char* file, int flags) { return _dlopen(file, flags); } +static void run_success_callback(void* user_data) { + struct async_data* data = (struct async_data*)user_data; + dlopen_onsuccess(data->dso, data); +} + +static void run_error_callback(void* user_data) { + struct async_data* data = (struct async_data*)user_data; + dlopen_onerror(data->dso, data); +} + void emscripten_dlopen(const char* filename, int flags, void* user_data, em_dlopen_callback onsuccess, em_arg_callback_func onerror) { dbg("emscripten_dlopen: %s", filename); @@ -608,30 +620,30 @@ void emscripten_dlopen(const char* filename, int flags, void* user_data, onsuccess(user_data, head->dso); return; } + + // Struct used to hold info on for the async callback + struct async_data* d = malloc(sizeof(struct async_data)); + d->user_data = user_data; + d->onsuccess = onsuccess; + d->onerror = onerror; + do_write_lock(); char buf[2*NAME_MAX+2]; filename = find_dylib(buf, filename, sizeof buf); - struct dso* p = find_existing(filename); - if (p) { - onsuccess(user_data, p); + d->dso = find_existing(filename); + if (d->dso) { + emscripten_queue_microtask(run_success_callback, d); return; } - p = load_library_start(filename, flags); - if (!p) { - do_write_unlock(); - onerror(user_data); + d->dso = load_library_start(filename, flags); + if (!d->dso) { + emscripten_queue_microtask(run_error_callback, d); return; } - // For async mode - struct async_data* d = malloc(sizeof(struct async_data)); - d->user_data = user_data; - d->onsuccess = onsuccess; - d->onerror = onerror; - dbg("calling emscripten_dlopen_js %p", p); // Unlock happens in dlopen_onsuccess/dlopen_onerror - _emscripten_dlopen_js(p, dlopen_onsuccess, dlopen_onerror, d); + _emscripten_dlopen_js(d->dso, dlopen_onsuccess, dlopen_onerror, d); } static void promise_onsuccess(void* user_data, void* handle) { diff --git a/test/other/test_dlopen_promise.c b/test/other/test_dlopen_promise.c index ae368e0f068e6..7415a18d14d8b 100644 --- a/test/other/test_dlopen_promise.c +++ b/test/other/test_dlopen_promise.c @@ -6,12 +6,25 @@ #include #include +void load_side_module(); + +int load_count = 0; +bool in_callback = false; + em_promise_result_t on_fullfilled(void **result, void* data, void *handle) { - printf("onsuccess\n"); + printf("onsuccess: %d\n", load_count++); int* foo = (int*)dlsym(handle, "foo"); assert(foo); printf("foo = %d\n", *foo); assert(*foo == 42); + + in_callback = true; + if (load_count < 2) { + // Load the same again, and make sure we don't re-enter ourselves. + load_side_module(); + } + in_callback = false; + return EM_PROMISE_FULFILL; } @@ -20,11 +33,15 @@ em_promise_result_t on_rejected(void **result, void* data, void *value) { return EM_PROMISE_FULFILL; } -int main() { +void load_side_module() { em_promise_t inner = emscripten_dlopen_promise("libside.so", RTLD_NOW); em_promise_t outer = emscripten_promise_then(inner, on_fullfilled, on_rejected, NULL); emscripten_promise_destroy(outer); emscripten_promise_destroy(inner); +} + +int main() { + load_side_module(); printf("returning from main\n"); return 0; } diff --git a/test/other/test_dlopen_promise.out b/test/other/test_dlopen_promise.out index 1872f13fa4b4b..106d12bef7b96 100644 --- a/test/other/test_dlopen_promise.out +++ b/test/other/test_dlopen_promise.out @@ -1,3 +1,5 @@ returning from main -onsuccess +onsuccess: 0 +foo = 42 +onsuccess: 1 foo = 42