-
Notifications
You must be signed in to change notification settings - Fork 13.3k
Run TLS destructors for wasm32-wasip1-threads make Node.js stuck #137510
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
The wasm32-wasip1-threads target enables the atomics feature by default, so it's always used (unless someone unsoundly turns it off).
|
I misunderstood, there should be other reasons for this. In the Node.js context, I suspect it's because |
Could you reproduce this without node.js, i.e. minimal example running on wasmtime? |
@surban it works fine on wasmtime, I think this might be an issue specific to Node.js/Browser |
I don't know how exactly emnapi is emulating threads, but since this works fine with native threads on wasmtime, I suspect the issue is with emnapi and not Rust. |
The logic stuck before going into emnapi, there is no emnapi and NAPI-RS in my repro: https://github.com/Brooooooklyn/rust-wasi-thread-local-stuck use std::cell::RefCell;
use std::collections::HashMap;
thread_local! {
static THREAD_LOCAL_DATA: RefCell<HashMap<u32, u32>> = RefCell::new(HashMap::new());
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn set_thread_local_data(key: u32, value: u32) {
THREAD_LOCAL_DATA.with(|data| {
data.borrow_mut().insert(key, value);
});
} import { WASI } from 'node:wasi'
import fs from 'node:fs'
const wasi = new WASI({
version: 'preview1',
env: process.env,
})
const wasm = await WebAssembly.compile(
new Uint8Array(fs.readFileSync('target/wasm32-wasip1-threads/debug/wasi_thread_local.wasm')),
)
const instance = await WebAssembly.instantiate(wasm, {
...wasi.getImportObject(),
env: {
memory: new WebAssembly.Memory({ initial: 1024, maximum: 65536, shared: true }),
},
})
wasi.initialize(instance);
console.log(instance.exports.set_thread_local_data(1, 2)) |
I've updated my repro and added browser examples, it stuck in FireFox & Chrome |
You are targeting |
At least all modern browsers support And it stucks on Chrome too, my Chrome version: |
No web browser currently has native support for WASI and the polyfill you are using does not seem to have support for threads (see list of implemented syscalls at the end of the page). At the moment I am not convinced that your environment has a complaint implementation of atomics and threads. The fact that your example is working fine using |
Also, one more thing that struck me as odd: You seem to be calling functions inside a WASI module. This is only supported when the module is a WASI reactor, however, by default Rust builds a WASI command. Your can change this by setting your RUSTFLAGS accordingly in [target.wasm32-wasip1-threads]
rustflags = ["-Z", "wasi-exec-model=reactor"] |
After breakpoint debugging in @toyobayashi, we determined that the issue was caused by not setting My next question is, if the wasi |
Do you have intuition for why |
@alexcrichton Context: toyobayashi/emnapi#136 (comment)
cause infinit loop here: https://github.com/WebAssembly/wasi-libc/blob/e9524a0980b9bb6bb92e87a41ed1055bdda5bb86/libc-top-half/musl/src/thread/pthread_key_create.c#L59-L60
@Brooooooklyn wasmtime also does try to get
|
My guess would be that for a WASI command the CRT performs cleanup after The spec says:
I don't see a way to legally call functions inside a WASI module without setting its type to reactor. |
@Brooooooklyn Update: Using wasmtime to run your example also get stuck. cargo build --target wasm32-wasip1-threads
wasmtime run \
-S threads=y \
-W threads=y \
--invoke set_thread_local_data \
./target/wasm32-wasip1-threads/debug/wasi_thread_local.wasm \
1 2 The result is the same no matter it is in Node.js or native. I think the problem is clear now (#137510 (comment))
Back to this question, for WASI reactor, since wasi-libc has been using |
Ah ok yeah that makes sense to me, some de-initializing of data in wasi-libc probably isn't expected to be run from multiple threads or then re-run on de-initialized state. FWIW I don't think there's a path forward for stabilizing The problem is that |
I understand that |
It's true that it doesn't block anything per-se, but it'll make it harder to drum up support and folks willing to champion stabilization. I personally wouldn't block such an effort of course, but I agree that renaming it would probably be best. |
I tried this code:
https://github.com/Brooooooklyn/rust-wasi-thread-local-stuck
I expected to see this happen: Program run and exit normally
Instead, this happened: hang forever
(edit): See toyobayashi/emnapi#136 (comment) for where and why
Meta
rustc --version --verbose
:Background
The NAPI-RS project enables the compilation of Rust projects to the
wasm32-wasip1-threads
target, allowing them to run in Node.js. However, since Node.js doesn’t natively support WASI threads, we simulate a threaded environment using emnapi. This setup worked effectively until version1.85.0
. Many projects, such as rolldown and rspack, have been successfully compiled to WebAssembly targets to run seamlessly on platforms like StackBlitz and in web browsers.The text was updated successfully, but these errors were encountered: