【问题标题】:Creating web worker from Rust with Emscripten target使用 Emscripten 目标从 Rust 创建 Web Worker
【发布时间】:2017-12-28 02:53:26
【问题描述】:

我正在尝试从 Rust 创建一个 web worker,调用 worker 文件中的一个函数并将一些数据传递给主线程。

ma​​in.rs

mod externs;
extern crate libc;

fn main() {
    println!("starting worker");
    let worker = externs::create_worker("./worker.js");
    externs::call_worker(worker, "worker_fn", "", 0);
    println!("worker called");
}

worker.rs

#![feature(link_args)]
#[link_args = "-s EXPORTED_FUNCTIONS=['_worker_fn'] -s BUILD_AS_WORKER=1"]

extern crate libc;

mod externs;

extern {}

fn main() {
    println!("worker main");
}

#[no_mangle]
pub extern fn worker_fn() {
    println!("hello from the other side!");
}

当我编译 worker 和 main 文件时,我可以看到来自 main.rs 的消息,甚至可以看到 worker 文件中的“worker main”消息。我还可以看到浏览器向worker.js发送了一个请求,但似乎主线程没有调用worker文件中的worker_fn

这是externs 文件:

use std::ffi::CString;
use libc::*;
use std::str::FromStr;

/// Creating web worker
pub fn create_worker(url: &str) -> ffi::worker_handle {
    let url = CString::new(url).unwrap();
    let ptr = url.as_ptr();
    unsafe { ffi::emscripten_create_worker(ptr) }
}

extern "C" fn do_something_handler(arg1: *mut c_char, arg2: c_int, arg3: *mut c_void) {
    println!("worker done!");
}

/// Creating web worker
pub fn call_worker(worker: ffi::worker_handle, func_name: &str, data: &str, size: i32) {
    let func_name = CString::new(func_name).unwrap();

    let mut string = String::from_str(data).unwrap();
    let bytes = string.into_bytes();
    let mut cchar : Vec<c_char> = bytes.iter().map(|&w| w as c_char).collect();
    let data_slice = cchar.as_mut_slice();

    let mut state = 42;
    let state_ptr: *mut c_void = &mut state as *mut _ as *mut c_void;

    unsafe {
        ffi::emscripten_call_worker(
            worker,
            func_name.as_ptr(),
            data_slice.as_mut_ptr(),
            size as c_int,
            Some(do_something_handler),
            state_ptr
        )
    };
}

// This is mostly standard Rust-C FFI stuff.
mod ffi {
    use libc::*;
    pub type worker_handle = c_int;
    pub type em_worker_callback_func = Option<unsafe extern "C" fn(arg1: *mut c_char,
                                                                   arg2: c_int,
                                                                   arg3: *mut c_void)>;

    extern "C" {
        pub fn emscripten_run_script_int(x: *const c_char) -> c_int;
        pub fn emscripten_create_worker(url: *const c_char) -> worker_handle;
        pub fn emscripten_call_worker(
            worker: worker_handle,
            funcname: *const c_char,
            data: *mut c_char,
            size: c_int,
            callback: em_worker_callback_func,
            arg: *mut c_void
        );
        pub fn emscripten_worker_respond(data: *mut c_char, size: c_int);
        pub fn emscripten_worker_respond_provisionally(data: *mut c_char, size: c_int);
    }
}

我不明白问题出在哪里。我应该以某种方式更改工作文件甚至link_args

【问题讨论】:

    标签: rust emscripten


    【解决方案1】:

    我通过像这样使用stdweb crate 解决了这个问题(感谢ivanceras):

    worker.rs

    #![feature(link_args)]
    #[link_args = "-s BUILD_AS_WORKER=1"]
    
    #[macro_use]
    extern crate stdweb;
    
    fn main(){
        stdweb::initialize();
    
        js! {
            this.addEventListener("message", (e) => {
                console.log("The main thread said something", e.data);
            })
        }
        stdweb::event_loop();
    }
    

    loader.js

    var wasm_file = "worker.wasm"; // wasm file
    var wjs_file = "worker.js"; // w.js file that links the wasm file
    
    Module = {}
    console.log("Loading webassembly version");
    /// fetch wasm file and inject the js file
    fetch(wasm_file)
      .then(response => response.arrayBuffer())
      .then(bytes => {
        Module.wasmBinary = bytes;
        console.log("wasm has loaded..");
        console.log("attaching as script");
        self.importScripts(wjs_file);
      });
    

    最后是 HTML 文件

    <script>
        var worker = new Worker("loader.js");
        setTimeout(function () {
            worker.postMessage({"cmd":"doSomething"});
        }, 1000);
    </script>
    

    在构建 Rust 文件时不要忘记添加 --target=wasm32-unknown-emscripten 标志。

    【讨论】:

    • 帮助我理解为什么worker.js 是必要的。如果loader.jsWorker 中运行,为什么不直接加载wasm 文件?相反,它使用importScripts 移交给应该加载wasm 文件的worker.js。为什么会有这个额外的间接层?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多