如何在Rust编写的WebAssembly模块中保留内部状态?

时间:2017-12-08 21:13:18

标签: rust webassembly

我想在我的网络应用的每一帧上对大量数据进行计算。 JavaScript只会使用其中的一部分,因此,不是每帧在WebAssembly和JavaScript之间来回发送整套数据,如果数据是在我的WebAssembly模块内部维护的话,那就太好了。

在C中,这样的东西起作用:

#include <emscripten/emscripten.h>

int state = 0;

void EMSCRIPTEN_KEEPALIVE inc() {
    state++;
}

int EMSCRIPTEN_KEEPALIVE get() {
    return state;
}

Rust可以做同样的事吗?我尝试使用static这样做:

static mut state: i32 = 0;

pub fn main() {}

#[no_mangle]
pub fn add() {
    state += 1;
}

#[no_mangle]
pub fn get() -> i32 {
    state
}

但似乎static变量不可变。

2 个答案:

答案 0 :(得分:2)

Francis Gagné is absolutely correct全局变量通常会使您的代码变得更糟,您应该避免使用它们。

但是,对于WebAssembly的特定情况,因为今天,我们不必担心这个问题:

  

如果您有多个帖子

我们可以选择使用可变的静态变量,如果我们有充分的理由这样做:

// Only valid because we are using this in a WebAssembly
// context without threads.
static mut STATE: i32 = 0;

#[no_mangle]
pub extern fn add() {
    unsafe { STATE += 1 };
}

#[no_mangle]
pub extern fn get() -> i32 {
    unsafe { STATE }
}

我们可以看到这个NodeJS驱动程序的行为:

const fs = require('fs-extra');

fs.readFile(__dirname + '/target/wasm32-unknown-unknown/release/state.wasm')
  .then(bytes => WebAssembly.instantiate(bytes))
  .then(({ module, instance }) => {
    const { get, add } = instance.exports;
    console.log(get());
    add();
    add();
    console.log(get());
});
0
2

答案 1 :(得分:1)

error[E0133]: use of mutable static requires unsafe function or block

通常,访问可变全局变量是 unsafe ,这意味着您只能在unsafe块中执行此操作。使用可变的全局变量,很容易意外地创建悬空引用(想想对全局可变Vec项的引用),数据竞争(如果你有多个线程 - Rust没有&t; t小心你没有实际使用线程)或以其他方式调用undefined behavior

全局变量通常不是问题的最佳解决方案,因为它会降低您的软件灵活性并降低可重用性。相反,请考虑明确地(通过引用,因此您不需要将其复制)传递给需要对其进行操作的函数。这使得调用代码可以使用多个独立状态。

这是分配唯一状态并修改它的示例:

type State = i32;

#[no_mangle]
pub extern fn new() -> *mut State {
    Box::into_raw(Box::new(0))
}

#[no_mangle]
pub extern fn free(state: *mut State) {
    unsafe { Box::from_raw(state) };
}

#[no_mangle]
pub extern fn add(state: *mut State) {
    unsafe { *state += 1 };
}

#[no_mangle]
pub extern fn get(state: *mut State) -> i32 {
    unsafe { *state }
}
const fs = require('fs-extra');

fs.readFile(__dirname + '/target/wasm32-unknown-unknown/release/state.wasm')
  .then(bytes => WebAssembly.instantiate(bytes))
  .then(({ module, instance }) => {
    const { new: newFn, free, get, add } = instance.exports;

    const state1 = newFn();
    const state2 = newFn();

    add(state1);
    add(state2);
    add(state1);

    console.log(get(state1));
    console.log(get(state2));

    free(state1);
    free(state2);
});
2
1

注意 - 当前需要在发布模式下编译才能工作。调试模式目前存在一些问题。

不可否认,这并不是 less 不安全,因为你正在传递原始指针,但它使调用代码更清楚地表明存在一些可变状态被操纵。另请注意,现在调用者负责确保正确处理状态指针。