emscripten-core / emscripten-core/emscripten
Runtime crash when calling poll() from a worker thread with pthreads and Asyncify
- Dominant language
- C++
- Stars
- 27.6k
- Forks
- 3.6k
- Avg merge
- 1d 1h
- Merged PRs (30d)
- 105
Description
## Summary
Calling `poll()` from a pthread worker thread crashes at runtime when the program is built with `-pthread` and `-sASYNCIFY`.
## Version of emscripten/emsdk
```
$ emcc -v
emcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) 6.0.3-git (22383d7400b93ea4404b72d6cbe0bff837b8dc22)
clang version 23.0.0git (https:/github.com/llvm/llvm-project 4708581453ead90685a55e343cc5ace1e1ddabb1)
Target: wasm32-unknown-emscripten
Thread model: posix
InstalledDir: /root/emsdk/upstream/bin
Build config: +assertions
```
Node.js used to run the built output: `v26.2.0`
## Repro source
```c
#include
#include
#include
static void *poll_thread(void *arg) {
(void)arg;
puts("[c] poll start");
int rc = poll(NULL, 0, 1);
printf("[c] poll rc=%d\n", rc);
return NULL;
}
int main(void) {
pthread_t t;
pthread_create(&t, NULL, poll_thread, NULL);
pthread_join(t, NULL);
puts("[c] done");
return 0;
}
```
## Expected behavior
```
[c] poll start
[c] poll rc=0
[c] done
```
## Case 1: default build
### Failing command line in full
Link succeeds. Runtime fails with:
```bash
node dist/default.js
```
Output (exit code 1):
```
[c] poll start
Aborted(import _emscripten_receive_on_main_thread_js was not in ASYNCIFY_IMPORTS, but changed the state)
RuntimeError: Aborted(import _emscripten_receive_on_main_thread_js was not in ASYNCIFY_IMPORTS, but changed the state)
at abort (.../dist/default.js:1:15046)
at imports. (.../dist/default.js:1:86822)
at wasm://wasm/00036c6e:wasm-function[67]:0x5174
at wasm://wasm/00036c6e:wasm-function[60]:0x47fe
at wasm://wasm/00036c6e:wasm-function[29]:0x1c6e
at wasm://wasm/00036c6e:wasm-function[70]:0x53c2
at wasm://wasm/00036c6e:wasm-function[71]:0x5679
at wasm://wasm/00036c6e:wasm-function[38]:0x2410
at wasm://wasm/00036c6e:wasm-function[23]:0x12b9
at __asyncify_wrapper_23 (.../dist/default.js:1:87004)
```
(Abort also prints the minified `default.js` source line between the `Aborted(...)` message and the `RuntimeError`; omitted here.)
Without `-sASSERTIONS=2`, the same build exits with code 1 but does not print the `ASYNCIFY_IMPORTS` abort message.
### Full link command and output with `-v` appended
```bash
emcc main.c \
-O2 \
-pthread -sUSE_PTHREADS=1 \
-sASYNCIFY \
-sASSERTIONS=2 \
-sENVIRONMENT=node,web,worker \
-sEXIT_RUNTIME=1 \
-o dist/default.js \
-v
```
```
/root/emsdk/upstream/bin/clang -target wasm32-unknown-emscripten -fignore-exceptions -mllvm -combiner-global-alias-analysis=false -mllvm -enable-emscripten-sjlj -mllvm -disable-lsr --sysroot=/root/emsdk/upstream/emscripten/cache/sysroot -D__EMSCRIPTEN_SHARED_MEMORY__=1 -Xclang -iwithsysroot/include/fakesdl -Xclang -iwithsysroot/include/compat -O2 -pthread -v -c main.c -o /tmp/emscripten_temp_urode733/main.o
clang version 23.0.0git (https:/github.com/llvm/llvm-project 4708581453ead90685a55e343cc5ace1e1ddabb1)
Target: wasm32-unknown-emscripten
Thread model: posix
InstalledDir: /root/emsdk/upstream/bin
Build config: +assertions
(in-process)
"/root/emsdk/upstream/bin/clang-23" -cc1 -triple wasm32-unknown-emscripten -O2 -emit-obj -disable-free -clear-ast-before-backend -main-file-name main.c -mrelocation-model static -mframe-pointer=none -ffp-contract=on -fno-rounding-math -mconstructor-aliases -target-feature +atomics -target-feature +bulk-memory -target-feature +mutable-globals -target-feature +sign-ext -target-cpu generic -fvisibility=hidden -debugger-tuning=gdb -fdebug-compilation-dir=/root/emscripten-bug-poll -v -fcoverage-compilation-dir=/root/emscripten-bug-poll -resource-dir /root/emsdk/upstream/lib/clang/23 -D __EMSCRIPTEN_SHARED_MEMORY__=1 -isysroot /root/emsdk/upstream/emscripten/cache/sysroot -internal-isystem /root/emsdk/upstream/lib/clang/23/include -internal-isystem /root/emsdk/upstream/emscripten/cache/sysroot/include/wasm32-emscripten -internal-isystem /root/emsdk/upstream/emscripten/cache/sysroot/include -ferror-limit 19 -pthread -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fignore-exceptions -fno-color-diagnostics -vectorize-loops -vectorize-slp -iwithsysroot/include/fakesdl -iwithsysroot/include/compat -mllvm -combiner-global-alias-analysis=false -mllvm -enable-emscripten-sjlj -mllvm -disable-lsr -o /tmp/emscripten_temp_urode733/main.o -x c main.c
clang -cc1 version 23.0.0git based upon LLVM 23.0.0git default target x86_64-unknown-linux-gnu
ignoring nonexistent directory "/root/emsdk/upstream/emscripten/cache/sysroot/include/wasm32-emscripten"
#include "..." search starts here:
#include <...> search starts here:
/root/emsdk/upstream/emscripten/cache/sysroot/include/fakesdl
/root/emsdk/upstream/emscripten/cache/sysroot/include/compat
/root/emsdk/upstream/lib/clang/23/include
/root/emsdk/upstream/emscripten/cache/sysroot/include
End of search list.
/root/emsdk/upstream/bin/clang --version
emcc: warning: USE_PTHREADS is deprecated (prefer the standard -pthread flag). Please open a bug if you have a continuing need for this setting [-Wdeprecated]
/root/emsdk/upstream/bin/wasm-ld -o dist/default.wasm /tmp/tmpnlv4s8vslibemscripten_js_symbols.so -Bstatic --import-memory --shared-memory --export=emscripten_stack_get_end --export=emscripten_stack_get_free --export=emscripten_stack_get_base --export=emscripten_stack_get_current --export=emscripten_stack_init --export=_emscripten_stack_alloc --export=_emscripten_thread_free_data --export=_emscripten_thread_crashed --export=__funcs_on_exit --export=__wasm_call_ctors --export=_emscripten_tls_init --export=_emscripten_thread_init --export=pthread_self --export=__set_thread_state --export=_emscripten_stack_restore --export=emscripten_stack_set_limits --export=_emscripten_thread_exit --export=malloc --export=free --export-if-defined=__start_em_asm --export-if-defined=__stop_em_asm --export-if-defined=__start_em_lib_deps --export-if-defined=__stop_em_lib_deps --export-if-defined=__start_em_js --export-if-defined=__stop_em_js --export-if-defined=main --export-if-defined=__main_argc_argv --export-if-defined=fflush --export-table -z stack-size=65536 --no-growable-memory --initial-memory=16777216 --no-entry --no-stack-first --table-base=1 --global-base=1024 /tmp/emscripten_temp_urode733/main.o -L/root/emsdk/upstream/emscripten/cache/sysroot/lib/wasm32-emscripten -L/root/emsdk/upstream/emscripten/src/lib /root/emsdk/upstream/emscripten/cache/sysroot/lib/wasm32-emscripten/crtbegin-mt.o -lGL-mt-getprocaddr -lal -lhtml5 -lstubs-debug -lc-mt-debug -ldlmalloc-mt-debug -lclang_rt.builtins-mt -lc++-debug-mt-noexcept -lc++abi-debug-mt-noexcept -lsockets-mt -mllvm -combiner-global-alias-analysis=false -mllvm -enable-emscripten-sjlj -mllvm -disable-lsr
/root/emsdk/upstream/bin/llvm-objcopy dist/default.wasm dist/default.wasm '--remove-section=llvm.func_attr.annotate.*' --remove-section=producers '--remove-section=.debug*'
/root/emsdk/upstream/bin/wasm-emscripten-finalize --bigint --no-legalize-javascript-ffi --check-stack-overflow dist/default.wasm -o dist/default.wasm --detect-features
/root/emsdk/upstream/bin/wasm-opt --strip-target-features --post-emscripten -O2 --low-memory-unused --asyncify --pass-arg=asyncify-asserts --pass-arg=asyncify-propagate-addlist '--pass-arg=asyncify-imports@env.invoke_*,env.__asyncjs__*,*.fd_sync,*.emscripten_promise_await,*.emscripten_promise_await_unchecked,*.__syscall_poll,*.emscripten_idb_load,*.emscripten_idb_store,*.emscripten_idb_delete,*.emscripten_idb_exists,*.emscripten_idb_clear,*.emscripten_sleep,*.emscripten_wget_data,*.emscripten_scan_registers,*.emscripten_fiber_swap' --zero-filled-memory --pass-arg=directize-initial-contents-immutable dist/default.wasm -o dist/default.wasm --mvp-features --enable-threads --enable-bulk-memory --enable-bulk-memory-opt --enable-call-indirect-overlong --enable-multivalue --enable-mutable-globals --enable-nontrapping-float-to-int --enable-reference-types --enable-sign-ext
/root/emsdk/node/22.16.0_64bit/bin/node /root/emsdk/upstream/emscripten/tools/acorn-optimizer.mjs /tmp/emscripten_temp_urode733/default.js JSDCE --minify-whitespace -o /tmp/emscripten_temp_urode733/default.jso1.js
```
## Case 2: with `-sASYNCIFY_IMPORTS`
### Failing command line in full
Link succeeds. Runtime fails with:
```bash
node dist/importlisted.js
```
Output (exit code 1):
```
[c] poll start
TypeError: rtn.then is not a function
at __emscripten_receive_on_main_thread_js (.../dist/importlisted.js:1:80325)
at imports. (.../dist/importlisted.js:1:86583)
at wasm://wasm/000371fa:wasm-function[67]:0x527e
at wasm://wasm/000371fa:wasm-function[60]:0x47fe
at wasm://wasm/000371fa:wasm-function[29]:0x1c6e
at wasm://wasm/000371fa:wasm-function[70]:0x5525
at wasm://wasm/000371fa:wasm-function[71]:0x57dc
at wasm://wasm/000371fa:wasm-function[38]:0x2410
at wasm://wasm/000371fa:wasm-function[23]:0x12b9
at __asyncify_wrapper_23 (.../dist/importlisted.js:1:87047)
```
(Abort also prints the minified `importlisted.js` source line before the `TypeError`; omitted here.)
This happens at both `-O0` and `-O2` when the import is listed.
### Full link command and output with `-v` appended
```bash
emcc main.c \
-O2 \
-pthread -sUSE_PTHREADS=1 \
-sASYNCIFY \
-sASSERTIONS=2 \
-sENVIRONMENT=node,web,worker \
-sEXIT_RUNTIME=1 \
-sASYNCIFY_IMPORTS="['_emscripten_receive_on_main_thread_js']" \
-o dist/importlisted.js \
-v
```
```
/root/emsdk/upstream/bin/clang -target wasm32-unknown-emscripten -fignore-exceptions -mllvm -combiner-global-alias-analysis=false -mllvm -enable-emscripten-sjlj -mllvm -disable-lsr --sysroot=/root/emsdk/upstream/emscripten/cache/sysroot -D__EMSCRIPTEN_SHARED_MEMORY__=1 -Xclang -iwithsysroot/include/fakesdl -Xclang -iwithsysroot/include/compat -O2 -pthread -v -c main.c -o /tmp/emscripten_temp_6pouatn5/main.o
clang version 23.0.0git (https:/github.com/llvm/llvm-project 4708581453ead90685a55e343cc5ace1e1ddabb1)
Target: wasm32-unknown-emscripten
Thread model: posix
InstalledDir: /root/emsdk/upstream/bin
Build config: +assertions
(in-process)
"/root/emsdk/upstream/bin/clang-23" -cc1 -triple wasm32-unknown-emscripten -O2 -emit-obj -disable-free -clear-ast-before-backend -main-file-name main.c -mrelocation-model static -mframe-pointer=none -ffp-contract=on -fno-rounding-math -mconstructor-aliases -target-feature +atomics -target-feature +bulk-memory -target-feature +mutable-globals -target-feature +sign-ext -target-cpu generic -fvisibility=hidden -debugger-tuning=gdb -fdebug-compilation-dir=/root/emscripten-bug-poll -v -fcoverage-compilation-dir=/root/emscripten-bug-poll -resource-dir /root/emsdk/upstream/lib/clang/23 -D __EMSCRIPTEN_SHARED_MEMORY__=1 -isysroot /root/emsdk/upstream/emscripten/cache/sysroot -internal-isystem /root/emsdk/upstream/lib/clang/23/include -internal-isystem /root/emsdk/upstream/emscripten/cache/sysroot/include/wasm32-emscripten -internal-isystem /root/emsdk/upstream/emscripten/cache/sysroot/include -ferror-limit 19 -pthread -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fignore-exceptions -fno-color-diagnostics -vectorize-loops -vectorize-slp -iwithsysroot/include/fakesdl -iwithsysroot/include/compat -mllvm -combiner-global-alias-analysis=false -mllvm -enable-emscripten-sjlj -mllvm -disable-lsr -o /tmp/emscripten_temp_6pouatn5/main.o -x c main.c
clang -cc1 version 23.0.0git based upon LLVM 23.0.0git default target x86_64-unknown-linux-gnu
ignoring nonexistent directory "/root/emsdk/upstream/emscripten/cache/sysroot/include/wasm32-emscripten"
#include "..." search starts here:
#include <...> search starts here:
/root/emsdk/upstream/emscripten/cache/sysroot/include/fakesdl
/root/emsdk/upstream/emscripten/cache/sysroot/include/compat
/root/emsdk/upstream/lib/clang/23/include
/root/emsdk/upstream/emscripten/cache/sysroot/include
End of search list.
/root/emsdk/upstream/bin/clang --version
emcc: warning: USE_PTHREADS is deprecated (prefer the standard -pthread flag). Please open a bug if you have a continuing need for this setting [-Wdeprecated]
/root/emsdk/upstream/bin/wasm-ld -o dist/importlisted.wasm /tmp/tmprrk9ogzwlibemscripten_js_symbols.so -Bstatic --import-memory --shared-memory --export=emscripten_stack_get_end --export=emscripten_stack_get_free --export=emscripten_stack_get_base --export=emscripten_stack_get_current --export=emscripten_stack_init --export=_emscripten_stack_alloc --export=_emscripten_thread_free_data --export=_emscripten_thread_crashed --export=__funcs_on_exit --export=__wasm_call_ctors --export=_emscripten_tls_init --export=_emscripten_thread_init --export=pthread_self --export=__set_thread_state --export=_emscripten_stack_restore --export=emscripten_stack_set_limits --export=_emscripten_thread_exit --export=malloc --export=free --export-if-defined=__start_em_asm --export-if-defined=__stop_em_asm --export-if-defined=__start_em_lib_deps --export-if-defined=__stop_em_lib_deps --export-if-defined=__start_em_js --export-if-defined=__stop_em_js --export-if-defined=main --export-if-defined=__main_argc_argv --export-if-defined=fflush --export-table -z stack-size=65536 --no-growable-memory --initial-memory=16777216 --no-entry --no-stack-first --table-base=1 --global-base=1024 /tmp/emscripten_temp_6pouatn5/main.o -L/root/emsdk/upstream/emscripten/cache/sysroot/lib/wasm32-emscripten -L/root/emsdk/upstream/emscripten/src/lib /root/emsdk/upstream/emscripten/cache/sysroot/lib/wasm32-emscripten/crtbegin-mt.o -lGL-mt-getprocaddr -lal -lhtml5 -lstubs-debug -lc-mt-debug -ldlmalloc-mt-debug -lclang_rt.builtins-mt -lc++-debug-mt-noexcept -lc++abi-debug-mt-noexcept -lsockets-mt -mllvm -combiner-global-alias-analysis=false -mllvm -enable-emscripten-sjlj -mllvm -disable-lsr
/root/emsdk/upstream/bin/llvm-objcopy dist/importlisted.wasm dist/importlisted.wasm '--remove-section=llvm.func_attr.annotate.*' --remove-section=producers '--remove-section=.debug*'
/root/emsdk/upstream/bin/wasm-emscripten-finalize --bigint --no-legalize-javascript-ffi --check-stack-overflow dist/importlisted.wasm -o dist/importlisted.wasm --detect-features
/root/emsdk/upstream/bin/wasm-opt --strip-target-features --post-emscripten -O2 --low-memory-unused --asyncify --pass-arg=asyncify-asserts --pass-arg=asyncify-propagate-addlist '--pass-arg=asyncify-imports@env._emscripten_receive_on_main_thread_js,env.invoke_*,env.__asyncjs__*,*.fd_sync,*.emscripten_promise_await,*.emscripten_promise_await_unchecked,*.__syscall_poll,*.emscripten_idb_load,*.emscripten_idb_store,*.emscripten_idb_delete,*.emscripten_idb_exists,*.emscripten_idb_clear,*.emscripten_sleep,*.emscripten_wget_data,*.emscripten_scan_registers,*.emscripten_fiber_swap' --zero-filled-memory --pass-arg=directize-initial-contents-immutable dist/importlisted.wasm -o dist/importlisted.wasm --mvp-features --enable-threads --enable-bulk-memory --enable-bulk-memory-opt --enable-call-indirect-overlong --enable-multivalue --enable-mutable-globals --enable-nontrapping-float-to-int --enable-reference-types --enable-sign-ext
/root/emsdk/node/22.16.0_64bit/bin/node /root/emsdk/upstream/emscripten/tools/acorn-optimizer.mjs /tmp/emscripten_temp_6pouatn5/importlisted.js JSDCE --minify-whitespace -o /tmp/emscripten_temp_6pouatn5/importlisted.jso1.js
```
Contributor guide
Research direction
Reproduce the crash from the provided main.c using the default and ASYNCIFY_IMPORTS build commands, then compare the paths through __syscall_poll and _emscripten_receive_on_main_thread_js. Done means the pthread worker prints poll rc=0 and the program reaches [c] done without an abort or TypeError.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- c, node.js, wasm
- Domain
- compilers, operating-systems
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100