emscripten-core / emscripten-core/emscripten

main thread re-enter without Asyncify

Open
#17,874 5 comments 0 reactions 0 assignees View on GitHub
Dominant language
C++
Stars
27.6k
Forks
3.6k
Avg merge
1d 1h
Merged PRs (30d)
105

Description

I understand that when [Asyncify](https://emscripten.org/docs/porting/asyncify.html) is enabled, main thread can be re-entered during blocking calls. But I find that even when Asyncify is not enabled, re-enter still happens. This is a problem because in all other platforms, worker threads posting to main thread is not expected to run until main thread enters message loop. In our case, re-enter can happen when main thread is in a `scoped_lock`, causing deadlock. Simple work around is to replace `mutex` with `recursive_mutex`, but it adds unnecessary cost to other platforms.

Here's a simple program that shows the problem. You can play with it: https://github.com/jiulongw/emscripten-mutex-test

```cpp
#include
#include
#include

#include

std::recursive_mutex g_mutex;
std::mutex g_log;

void log(std::string msg) {
std::scoped_lock lk(g_log);
std::cout << "[" << std::this_thread::get_id() << "] " << msg << std::endl;
}

void run_in_main_thread() {
log("Enter run_in_main_thread.");

{
log("accquiring lock...");
std::scoped_lock lk(g_mutex);
log("lock accquired.");
}
log("lock released.");
}

void worker_proc() {
log("Enter worker_proc.");

emscripten_async_run_in_main_runtime_thread(EM_FUNC_SIG_V,
&run_in_main_thread);
}

int main() {
log("Enter main.");
std::thread worker(&worker_proc);

std::chrono::milliseconds sleep_time(10);
{
log("accquiring lock...");
std::scoped_lock lk(g_mutex);
log("lock accquired.");

std::this_thread::sleep_for(sleep_time);
}
log("lock released.");

worker.join();
return 0;
}
```

Typical output is:
```
1: [16324] Enter main.
2: [16324] accquiring lock...
3: [16324] lock accquired.
4: [5266552] Enter worker_proc.
5: [16324] Enter run_in_main_thread.
6: [16324] accquiring lock...
7: [16324] lock accquired.
8: [16324] lock released.
9: [16324] lock released.
```

As you can see, in line 3, main thread acquired the lock and started to sleep. In line 5 the same main thread started to do something else. If the `recursive_mutex` in the code were replaced with `mutex`, the program will deadlock.

**Version of emscripten/emsdk:**
```
emcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) 3.1.21 (f9e81472f1ce541eddece1d27c3632e148e9031a)
Copyright (C) 2014 the Emscripten authors (see AUTHORS.txt)
This is free and open source software under the MIT license.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
```

**Full link command and output with `-v` appended:**
```
$ emcc -std=c++17 -pthread -s PTHREAD_POOL_SIZE=2 -s ENVIRONMENT=web,worker main.cc -o index.html -v
"/Users/jiulongw/w/emsdk/upstream/bin/clang" --version
"/Users/jiulongw/w/emsdk/upstream/bin/clang++" -target wasm32-unknown-emscripten -fignore-exceptions -fvisibility=default -mllvm -combiner-global-alias-analysis=false -mllvm -enable-emscripten-sjlj -mllvm -disable-lsr -D__EMSCRIPTEN_SHARED_MEMORY__=1 -DEMSCRIPTEN -D__EMSCRIPTEN_major__=3 -D__EMSCRIPTEN_minor__=1 -D__EMSCRIPTEN_tiny__=21 -I/Users/jiulongw/w/emsdk/upstream/emscripten/cache/sysroot/include/SDL --sysroot=/Users/jiulongw/w/emsdk/upstream/emscripten/cache/sysroot -Xclang -iwithsysroot/include/compat -std=c++17 -pthread -v -matomics -mbulk-memory main.cc -c -o /var/folders/q0/bv2y31zx13x0wk6r7wvbkn100000gn/T/emscripten_temp_j4p8r721/main_0.o
clang version 16.0.0 (https://github.com/llvm/llvm-project a4a29438f451370ed241dde30bfcaab0fdf2ab71)
Target: wasm32-unknown-emscripten
Thread model: posix
InstalledDir: /Users/jiulongw/w/emsdk/upstream/bin
(in-process)
"/Users/jiulongw/w/emsdk/upstream/bin/clang-16" -cc1 -triple wasm32-unknown-emscripten -emit-obj -mrelax-all --mrelax-relocations -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name main.cc -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 -target-feature +atomics -target-feature +bulk-memory -mllvm -treat-scalable-fixed-error-as-warning -debugger-tuning=gdb -v -fcoverage-compilation-dir=/Users/jiulongw/w/emscripten-mutex-test -resource-dir /Users/jiulongw/w/emsdk/upstream/lib/clang/16.0.0 -D __EMSCRIPTEN_SHARED_MEMORY__=1 -D EMSCRIPTEN -D __EMSCRIPTEN_major__=3 -D __EMSCRIPTEN_minor__=1 -D __EMSCRIPTEN_tiny__=21 -I /Users/jiulongw/w/emsdk/upstream/emscripten/cache/sysroot/include/SDL -isysroot /Users/jiulongw/w/emsdk/upstream/emscripten/cache/sysroot -internal-isystem /Users/jiulongw/w/emsdk/upstream/emscripten/cache/sysroot/include/wasm32-emscripten/c++/v1 -internal-isystem /Users/jiulongw/w/emsdk/upstream/emscripten/cache/sysroot/include/c++/v1 -internal-isystem /Users/jiulongw/w/emsdk/upstream/lib/clang/16.0.0/include -internal-isystem /Users/jiulongw/w/emsdk/upstream/emscripten/cache/sysroot/include/wasm32-emscripten -internal-isystem /Users/jiulongw/w/emsdk/upstream/emscripten/cache/sysroot/include -std=c++17 -fdeprecated-macro -fdebug-compilation-dir=/Users/jiulongw/w/emscripten-mutex-test -ferror-limit 19 -fvisibility=default -pthread -fgnuc-version=4.2.1 -fcxx-exceptions -fignore-exceptions -fexceptions -fcolor-diagnostics -iwithsysroot/include/compat -mllvm -combiner-global-alias-analysis=false -mllvm -enable-emscripten-sjlj -mllvm -disable-lsr -o /var/folders/q0/bv2y31zx13x0wk6r7wvbkn100000gn/T/emscripten_temp_j4p8r721/main_0.o -x c++ main.cc
clang -cc1 version 16.0.0 based upon LLVM 16.0.0git default target x86_64-apple-darwin21.6.0
ignoring nonexistent directory "/Users/jiulongw/w/emsdk/upstream/emscripten/cache/sysroot/include/wasm32-emscripten/c++/v1"
ignoring nonexistent directory "/Users/jiulongw/w/emsdk/upstream/emscripten/cache/sysroot/include/wasm32-emscripten"
#include "..." search starts here:
#include <...> search starts here:
/Users/jiulongw/w/emsdk/upstream/emscripten/cache/sysroot/include/SDL
/Users/jiulongw/w/emsdk/upstream/emscripten/cache/sysroot/include/compat
/Users/jiulongw/w/emsdk/upstream/emscripten/cache/sysroot/include/c++/v1
/Users/jiulongw/w/emsdk/upstream/lib/clang/16.0.0/include
/Users/jiulongw/w/emsdk/upstream/emscripten/cache/sysroot/include
End of search list.
"/Users/jiulongw/w/emsdk/upstream/bin/wasm-ld" -o index.wasm /var/folders/q0/bv2y31zx13x0wk6r7wvbkn100000gn/T/emscripten_temp_j4p8r721/main_0.o -L/Users/jiulongw/w/emsdk/upstream/emscripten/cache/sysroot/lib/wasm32-emscripten /Users/jiulongw/w/emsdk/upstream/emscripten/cache/sysroot/lib/wasm32-emscripten/crtbegin.o -lGL-mt -lal -lhtml5 -lstubs-debug -lnoexit -lc-mt-debug -ldlmalloc-mt -lcompiler_rt-mt -lc++-mt-noexcept -lc++abi-mt-noexcept -lsockets-mt -mllvm -combiner-global-alias-analysis=false -mllvm -enable-emscripten-sjlj -mllvm -disable-lsr --import-undefined --import-memory --shared-memory --strip-debug --export-if-defined=main --export-if-defined=_emscripten_thread_init --export-if-defined=_emscripten_thread_exit --export-if-defined=_emscripten_thread_crashed --export-if-defined=_emscripten_tls_init --export-if-defined=pthread_self --export-if-defined=__start_em_asm --export-if-defined=__stop_em_asm --export-if-defined=__start_em_js --export-if-defined=__stop_em_js --export-if-defined=__main_argc_argv --export-if-defined=fflush --export=emscripten_stack_get_end --export=emscripten_stack_get_free --export=emscripten_stack_get_base --export=emscripten_stack_init --export=stackSave --export=stackRestore --export=stackAlloc --export=__wasm_call_ctors --export=__errno_location --export=emscripten_dispatch_to_thread_ --export=_emscripten_thread_free_data --export=emscripten_main_browser_thread_id --export=emscripten_main_thread_process_queued_calls --export=emscripten_run_in_main_runtime_thread_js --export=emscripten_stack_set_limits --export=__get_temp_ret --export=__set_temp_ret --export=malloc --export=free --export-table -z stack-size=5242880 --initial-memory=16777216 --no-entry --max-memory=16777216 --global-base=1024
"/Users/jiulongw/w/emsdk/upstream/bin/wasm-emscripten-finalize" --dyncalls-i64 --pass-arg=legalize-js-interface-exported-helpers index.wasm -o index.wasm --detect-features
"/Users/jiulongw/w/emsdk/node/14.18.2_64bit/bin/node" /Users/jiulongw/w/emsdk/upstream/emscripten/src/compiler.js /var/folders/q0/bv2y31zx13x0wk6r7wvbkn100000gn/T/tmp5767gmmo.json
"/Users/jiulongw/w/emsdk/upstream/bin/llvm-objcopy" index.wasm index.wasm --remove-section=.debug* --remove-section=producers
"/Users/jiulongw/w/emsdk/node/14.18.2_64bit/bin/node" /Users/jiulongw/w/emsdk/upstream/emscripten/tools/preprocessor.js /var/folders/q0/bv2y31zx13x0wk6r7wvbkn100000gn/T/emscripten_temp_j4p8r721/settings.js worker.js --expandMacros
"/Users/jiulongw/w/emsdk/node/14.18.2_64bit/bin/node" /Users/jiulongw/w/emsdk/upstream/emscripten/tools/preprocessor.js /var/folders/q0/bv2y31zx13x0wk6r7wvbkn100000gn/T/emscripten_temp_j4p8r721/settings.js shell.html
```

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.