Giter Site home page Giter Site logo

bubbler-4 / wasm-mt Goto Github PK

View Code? Open in Web Editor NEW

This project forked from w3reality/wasm-mt

0.0 1.0 0.0 1.4 MB

A multithreading library for Rust and WebAssembly

License: Apache License 2.0

Makefile 1.12% Rust 64.18% HTML 1.25% JavaScript 33.45%

wasm-mt's Introduction

wasm-mt

Docs | GitHub | Crate

crates MIT licensed CI

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.

Examples

  • wasm-mt-pool - Thread pool library based on wasm-mt. [ crate | source ]

You can run all the following apps in browser!

  • exec - How to use wasm_mt. [ live | source ]
  • fib - Computing a Fibonacci sequence with nested threads. [ live | source ]
  • executors - Minimal serial/parallel executors using wasm_mt. [ live | source ]
  • parallel - Julia set benchmark of serial/parallel executors. [ live | source ]
  • arraybuffers - Demo of using WasmMt::new_with_arraybuffers(). [ live | source ]

Background and implementation

The preceding seminal work entitled "Multithreading Rust and Wasm" by @alexcrichton centers on Web Workers, shared memory, and the WebAssembly threads proposal. Shared memory is built on top of SharedArrayBuffer whose availability across major browsers has been somewhat limited. Also, the rust-wasm thread implementation work, along with the threads proposal, seems still in progress.

On the contrary, Web Worker based multithreading in JavaScript has been well supported for a long time. After experimenting, we have come up to a Rust ergonomic multithreading solution that does not require SharedArrayBuffer. It just works across all major browsers today and we named it wasm-mt.

Internally, we use the postMessage() Web Worker API (through bindings provided by wasm-bindgen) to initialize spawned threads. And, importantly, we keep using postMessage() for dynamically sending Rust closures (serialized by serde_traitobject) to the spawned threads. By doing so, the parent thread can await the results of the closures executed in the spawned thread. We have found that this approach is highly flexible for extension, too. For example, it is straightforward to augment WasmMt::Thread to support more customized inter-thread communication patterns.

Note, however, that wasm-mt has some remarkable limitations compared to the ongoing shared memory based multithreading work led by wasm-bindgen. wasm-mt is not efficient in that it does not include support of the standard thread primitive operations:

  • shared memory based message passing and mutexes,
  • atomic instructions and efficient memory handling per the threads proposal.

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 pkg_js = "./pkg/exec.js"; // path to `wasm-bindgen`'s JS binding
let mt = WasmMt::new(pkg_js).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.

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

console_ln!("๐Ÿš€ parallel executor:");
for th in v {
    spawn_local(async move {
        console_ln!("starting a thread");
        let ans = exec!(th, move || Ok(JsValue::from(42))).await.unwrap();
        console_ln!("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)

wasm-mt's People

Contributors

j-devel avatar

Watchers

James Cloos avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.