Description
I'm seeing what appears to be an invalid Asyncify call stack when using dlopen to dynamically load a SIDE_MODULE which uses a JavaScript library with asyncify imports. The runtime errors seen are similar to those I see when the Asyncify stack is not large enough (see JS console output below) but I have tried a variety of large stack sizes.
This issue is only present when building as a SIDE_MODULE/MAIN_MODULE and using dlopen. Building the library as a regular callable module and invoking using ccall works as expected. See working example below.
Minimal reproducer
dummy_if.cpp (SIDE_MODULE)
#include <stdio.h>
#include <emscripten.h>
extern "C"
{
extern void dummyAsyncImport();
}
extern "C" EMSCRIPTEN_KEEPALIVE
int Dummy_Open(int *handle, void* extra)
{
// Causes RuntimeError: unreachable, on Asyncify.handleSleep wakeUp()
// Removing this, dummyAsyncImport() works asynchronously as expected
// See JS console below
printf("pre dummyAsyncImport\n");
dummyAsyncImport();
// Causes RuntimeError: function signature mismatch, *before* Asyncify.handleSleep wakeUp()
// See JS console below
printf("post dummyAsyncImport\n");
return 0;
}
$ em++ -o dummy_if.wasm -s SIDE_MODULE=1 -s EXPORT_ALL=1 dummy_if.cpp
$ mv dummy_if.wasm dummy_if.so
library-dummy.js (JS library required by SIDE_MODULE)
mergeInto(LibraryManager.library, {
dummyAsyncImport: function() {
return Asyncify.handleSleep(wakeUp => {
setTimeout(wakeUp, 3000);
});
}
});
dlopen_test.cpp (MAIN_MODULE)
#include <stdio.h>
#include <dlfcn.h>
int main()
{
printf("Hello dlopen test\n");
// dlopen
void* handle = dlopen("/dummy_if.so", RTLD_NOW | RTLD_LOCAL);
if (!handle) {
printf("Cannot open library: %s\n", dlerror());
return 1;
}
printf("dlopen'd\n");
// Load symbols
typedef int (*Dummy_Open_t)(int *pHandle, const void *pDetails);
Dummy_Open_t Dummy_Open = (Dummy_Open_t) dlsym(handle, "Dummy_Open");
char *dlsym_error = dlerror();
if (dlsym_error) {
printf("Cannot load symbol Dummy_Open: %s\n", dlsym_error);
dlclose(handle);
return 1;
}
int ifHandle = 0;
int err = Dummy_Open(&ifHandle, NULL);
printf("Dummy_Open err: 0x%08X\n", err);
dlclose(handle);
return 0;
}
$ em++ -o dlopen_test.html -s MAIN_MODULE=1 --preload-file dummy_if.so --use-preload-plugins -s EXPORT_ALL=1 --js-library ./library-dummy.js -s ASYNCIFY=1 -s 'ASYNCIFY_IMPORTS=["dummyAsyncImport"]' -s ASSERTIONS=1 -s ASYNCIFY_STACK_SIZE=40960 dlopen_test.cpp
RuntimeErros
Working example without SIDE_MODULE/dlopen
Build as a entry point library:
$ em++ -o dummy_if.html -s EXPORT_ALL=1 --js-library ./library-dummy.js -s ASYNCIFY=1 -s 'ASYNCIFY_IMPORTS=["dummyAsyncImport"]' -s ASSERTIONS=1 -s ASYNCIFY_STACK_SIZE=40960 dummy_if.cpp
Use ccall to invoke from JS console:
> await ccall("Dummy_Open", 'number', ['number', 'number'], { async : true });
Enviroment
$ em++ -v
emcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) 2.0.10
clang version 12.0.0 (/b/s/w/ir/cache/git/chromium.googlesource.com-external-github.com-llvm-llvm--project 445289aa63e1b82b9eea6497fb2d0443813a9d4e)
Target: x86_64-unknown-linux-gnu
Thread model: posix