GithubHelp home page GithubHelp logo

w3reality / wasm-mt Goto Github PK

View Code? Open in Web Editor NEW
192.0 7.0 13.0 1.52 MB

A multithreading library for Rust and WebAssembly

License: Apache License 2.0

Makefile 1.43% Rust 48.21% HTML 1.60% JavaScript 48.77%
wasm threading rust

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.3"

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

bubbler-4 avatar j-devel avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

wasm-mt's Issues

[question] Technically is it Thread or Process ?

It seems the library is for multithreading, but it uses web workers with isolated memories, which seem more like processes as they surely doesn't share the same memory address with the main thread.

Issue running exec example

I am getting

GET http://localhost:8000/deps/start-app.js net::ERR_ABORTED 404 (File not found)

What should I do? Thanks.

Compatibility with standard async

Can this be used as a general executor for futures? Or maybe to make rayon work on different workers?
I have been trying to find a way to make my program work on wasm, but since it has heavy computations, it take a very long time to process without multi-threading (There are some encryption decryption on very large files). So I made my program using the asynchronous paradigm of rust to pipeline my process, and a little bit of parallel processing with rayon (both of which work very well when compiled on windows). But when I compile it for Wasm, I rayon isn't able to launch its executor threads. So I was wondering if there was a way to make this executor work with rayon, or if I would have to re implement the functionality I need from rayon myself using this.

Macro, like `tokio::test`?

This project is ๐Ÿ”ฅ!

I've done some experiments with wasm-mt-test and it's working great. However, I need to adjust my test code to run in a worker thread. It would be super cool if I could include a macro, like use wasm_mt_test::test and then my tests would automatically run in a worker.

Unless I'm mistaken, I believe it'd just generate a wrapper containing:

let pkg_js_uri = wasm_mt_test::get_pkg_js_uri();

let th = wasm_mt_test::create_mt(&pkg_js_uri).await.thread().and_init().await.unwrap();

exec!(th, async move || test_fn().await).await

Failed on running examples

start-app thrown an exception:

Uncaught (in promise) TypeError: Cannot read properties of null (reading 'src')
    at eval (eval at default (start-app.js:6:19), <anonymous>:10:53)
    at eval (eval at default (start-app.js:6:19), <anonymous>:675:3)
    at default (start-app.js:8:17)

An API to detect crash inside worker thread

I found that the current Thread API does not support the situation where the worker crashes while running the given task. (Please correct me if I'm wrong) So I made a patched version inside a small demo app (live version here). The patch adds an onerror hook to the created JS Worker instance, and an is_terminated() method to allow polling if the thread crashed or not.

Should I open a PR on this?

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.