wasm-mt

Docs | GitHub | Crate

A multithreading library for Rust and WebAssembly.

wasm-mt helps you create and execute Web Worker based threads. You can program the threads simply using Rust closures and orchestrate them with async/await.

Thanks:

Getting started

Requirements:

Cargo.toml:

wasm-mt = "0.1" serde = { version = "1.0", features = ["derive"] } serde_closure = "0.2"

Creating a thread

First, create a [WasmMt] thread builder with [new][WasmMt::new] and initialize it:

``` use wasm_mt::prelude::*;

let pkgjs = "./pkg/exec.js"; // path to wasm-bindgen's JS binding let mt = WasmMt::new(pkgjs).and_init().await.unwrap(); ```

Then, create a [wasm_mt::Thread][Thread] with the [thread][WasmMt::thread] function and initialize it:

let th = mt.thread().and_init().await.unwrap();

Executing a thread

Using the [exec!] macro, you can execute a closure in the thread and await the result:

``` // fn add(a: i32, b: i32) -> i32 { a + b }

let a = 1; let b = 2; let ans = exec!(th, move || { let c = add(a, b);

Ok(JsValue::from(c))

}).await?; assert_eq!(ans, JsValue::from(3)); ```

You can also execute an [async closure] with exec!:

``` // use wasm_mt::utils::sleep; // async fn sub(a: i32, b: i32) -> i32 { // sleep(1000).await; // a - b // }

let a = 1; let b = 2; let ans = exec!(th, async move || { let c = sub(a, b).await;

Ok(JsValue::from(c))

}).await?; assert_eq!(ans, JsValue::from(-1)); ```

Executing JavaScript in a thread

Using the [exec_js!] macro, you can execute JavaScript within a thread:

let ans = exec_js!(th, " const add = (a, b) => a + b; return add(1, 2); ").await?; assert_eq!(ans, JsValue::from(3));

Similarly, use [exec_js_async!] for running asynchronous JavaScript:

let ans = exec_js_async!(th, " const sub = (a, b) => new Promise(resolve => { setTimeout(() => resolve(a - b), 1000); }); return await sub(1, 2); ").await?; assert_eq!(ans, JsValue::from(-1));

Making executors

By using [wasm_mt:Thread][Thread], you can easily create custom executors. One such example is the wasm-mt-pool crate. It provides a thread pool that is based on the [work stealing] scheduling strategy.

Here, for simplicity, we show the implementation of much more straightforward executors: a serial executor and a parallel executor.

First, prepare a Vec<wasm_mt::Thread> containing initialized threads:

let mut v: Vec<wasm_mt::Thread> = vec![]; for i in 0..4 { let th = mt.thread().and_init().await?; v.push(th); }

Then, here's the executors in action. Note, in the latter case, we are using wasm_bindgen_futures::spawn_local to dispatch the threads in parallel.

``` consoleln!("🔥 serial executor:"); for th in &v { consoleln!("starting a thread"); let ans = exec!(th, move || Ok(JsValue::from(42))).await?; console_ln!("ans: {:?}", ans); }

consoleln!("🔥 parallel executor:"); for th in v { spawnlocal(async move { consoleln!("starting a thread"); let ans = exec!(th, move || Ok(JsValue::from(42))).await.unwrap(); consoleln!("ans: {:?}", ans); }); } ```

Observe the starting/ending timing of each thread in the developer console:

🔥 serial executor: starting a thread ans: JsValue(42) starting a thread ans: JsValue(42) starting a thread ans: JsValue(42) starting a thread ans: JsValue(42) 🔥 parallel executor: (4) starting a thread (4) ans: JsValue(42)