WebAssembly

最近在做 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
2
3
4
5
6
7
8
# Get the emsdk repo
git clone https://github.com/juj/emsdk.git

# Enter that directory
cd emsdk

# Activate PATH and other environment variables in the current terminal
source ./emsdk_env.sh

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.jswasm.wasm,其中wasm.js已经在hello.html中被引用,wasm.wasm会在wasm.js中被fetch, compile and instantiate

  • 直接打开hello.html看到输出

目前踩到的一些比较棘手的坑(嗯小坑不断)

  1. Calling derived JavaScript object inside another thread causes binding error
    • 这个是我遇到的一个比较严重的问题,简单来说就是在embindwebidl的环境下,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的一些便利性
  2. embind not maintained?

Reference

Thank you for making the world a better place!