oneTBB
oneTBB copied to clipboard
Wasm. Workers die after completing first task
Version of emscripten/emsdk: 3.1.74 tbb*: 2022.1.0 Multithread build.
We use tbb with emscripten. We create a pool of workers. We submit the first task and get the result in 0.7 s (multithreading works) Then all workers die and the next task runs on only one worker. 1.5 s (multithreading does not work)
Demo project https://github.com/Liahimz/MinimalWasm
https://github.com/emscripten-core/emscripten/issues/24412
Thank you for reporting this issue, @Alexufo.
May I please clarify whether a "pool of workers" is referring to a TBB task arena and a "task" is referring to arena.execute([&]{tbb::parallel_for(...);}). If so, then do you see no multithreading with every single task after the first one?
Since there is no parallelism guarantee in TBB (https://oneapi-spec.uxlfoundation.org/specifications/oneapi/latest/elements/onetbb/source/task_scheduler.html), some of these parallel_for calls might execute entirely within the user thread, without involving the workers.
Yes, that's correct. In our project, the process function launches parallel code like this:
arena.execute([&]{tbb::parallel_for(...);}).
After the first call to process, we encounter this error from the autogenerated wasm module:
wasm.js:9 main thread called exit(undefined): keepRuntimeAlive=false (counter=0),
and all web workers are terminated.
As a result, all subsequent calls to process run in single-threaded mode.
However, if we wrap calls to the process function in JS with a keep-alive mechanism, for example:
std::atomic<bool> keep_alive{false};
void idle_main_loop() {
// No-op
}
EMSCRIPTEN_KEEPALIVE void start_keepalive_mainloop() {
if (!keep_alive.exchange(true)) {
emscripten_set_main_loop(idle_main_loop, 1, 1);
}
}
EMSCRIPTEN_KEEPALIVE void stop_keepalive_mainloop() {
if (keep_alive.exchange(false)) {
emscripten_cancel_main_loop();
}
}
…then we get this warning from the JS module:
wasm.js:9 runtimeKeepalivePush -> counter=1, and the threads remain alive. With this approach, all subsequent calls to process run in parallel as expected.