最近在做 WebAssembly 相关的事情,边做边总结吧
Glossary
- WebAssembly: WebAssembly (abbreviated Wasm) is a binary instruction format for a stack-based virtual machine. Wasm is designed as a portable target for compilation of high-level languages like C/C++/Rust, enabling deployment on the web for client and server applications.
- emscripten: Emscripten is an Open Source LLVM to JavaScript compiler.
- emsdk: The Emscripten SDK (emsdk) is used to perform all SDK maintenance and can install, update, add, remove and activate SDKs and tools.
- emcc: The Emscripten Compiler Frontend (emcc). Emscripten’s drop-in replacement for a compiler like
gcc
.
我做的事情是希望在Web环境中用到C++的一个库,所以套用上面的术语,就是我会用利用emsdk
提供的环境,使用emcc
将C++
编译成WebAssembly
格式, 然后在Web
环境中使用.
image source: https://kripken.github.io/emscripten-site/docs/introducing_emscripten/about_emscripten.html
Browser compatibility
实践中我们比较关心的关于Thread的兼容性:
Tables | Available version | Conditions |
---|---|---|
Google chrome | Chrome 70 | Turn on experimental “WebAssembly threads support” flag |
Mozilla firefox | Firefox nightly channel | Turn on “javascript.options.shared_memory” flag |
Emscripten has support for multithreading using the new SharedArrayBuffer capability in browsers. Note that SharedArrayBuffer was disabled by default in all major browsers on 5 January, 2018 in response to Spectre. Chrome re-enabled it in v67 on platforms where its site-isolation feature is enabled to protect against Spectre-style vulnerabilities.
Setup
1 | # Get the emsdk repo |
Sample JavaScript-C++ interoperability code
hello.cc
#include <pthread.h> #include <iostream> extern "C" { extern int js_func(); void* test(void*) { std::cout << "Background thread" << std::endl; std::cout << js_func() << std::endl; return NULL; } void cpp_func() { pthread_t t; pthread_create(&t, NULL, &test, NULL); std::cout << "Main thread" << std::endl; } }
hello.js
Module.onRuntimeInitialized = () => { const cpp_func = Module.cwrap('cpp_func', null); cpp_func(); };
hello.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <script src="wasm.js"></script> <script src="hello.js"></script> </body> </html>
export.js
mergeInto(LibraryManager.library, { js_func: function () { return 10; }, });
Build
emcc hello.cc -o wasm.js -s EXPORTED_FUNCTIONS='["_cpp_func"]' -s EXTRA_EXPORTED_RUNTIME_METHODS='["cwrap"]' --js-library export.js -std=c++14 -s USE_PTHREADS=1 -s PTHREAD_POOL_SIZE=2 -g4
Build完之后会生成
wasm.js
和wasm.wasm
,其中wasm.js
已经在hello.html
中被引用,wasm.wasm
会在wasm.js
中被fetch
,compile
andinstantiate
直接打开
hello.html
看到输出
坑
目前踩到的一些比较棘手的坑(嗯小坑不断)
- Calling derived JavaScript object inside another thread causes binding error
- 这个是我遇到的一个比较严重的问题,简单来说就是在
embind
和webidl
的环境下,JavaScript能够实现C++中定义的interface,但却无法在C++的multi-thread环境中被调用 - 错误信息是JavaScript的Web workder环境无法识别
Module
中JavaScript中实现的Object - debug过程中发现,multi-thread环境下生成的
pthread-main.js
(worker)中,Module
并不包含JavaScript实现的Object,具体的重现过程可以参考我在上面链接中的提出的issue - 绕过这个坑的方法:目前使用纯C的接口可以绕过这个,当然不可避免的损失了C++ class的一些便利性
- 这个是我遇到的一个比较严重的问题,简单来说就是在
- embind not maintained?
Reference
- Glossary
- Emscripten issues
- WebIDL
- Embind
- Test suite:
- WebAssembly + Web worker
- WebWorker
- Big projects: