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
#include <poll.h>
#include <pthread.h>
#include <stdio.h>
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:
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.<computed> (.../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
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:
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.<computed> (.../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
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
Summary
Calling
poll()from a pthread worker thread crashes at runtime when the program is built with-pthreadand-sASYNCIFY.Version of emscripten/emsdk
Node.js used to run the built output:
v26.2.0Repro source
Expected behavior
Case 1: default build
Failing command line in full
Link succeeds. Runtime fails with:
Output (exit code 1):
(Abort also prints the minified
default.jssource line between theAborted(...)message and theRuntimeError; omitted here.)Without
-sASSERTIONS=2, the same build exits with code 1 but does not print theASYNCIFY_IMPORTSabort message.Full link command and output with
-vappendedCase 2: with
-sASYNCIFY_IMPORTSFailing command line in full
Link succeeds. Runtime fails with:
Output (exit code 1):
(Abort also prints the minified
importlisted.jssource line before theTypeError; omitted here.)This happens at both
-O0and-O2when the import is listed.Full link command and output with
-vappendedemcc 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