Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add multithreading example #2171

Merged
merged 14 commits into from
Nov 23, 2021
5 changes: 5 additions & 0 deletions .github/workflows/publish-examples.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ jobs:
continue
fi

# web_worker_fib does not compile with trunk. See <https://github.com/thedodd/trunk/issues/40>.
voidpumpkin marked this conversation as resolved.
Show resolved Hide resolved
if [[ "$example" == "web_worker_fib" ]]; then
continue
fi

echo "building: $example"
(
cd "$path"
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ members = [
"examples/todomvc",
"examples/two_apps",
"examples/webgl",
"examples/web_worker_fib",

# Release tools
"packages/changelog",
Expand Down
8 changes: 7 additions & 1 deletion examples/multi_thread/README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
# Warning

The multi-thread example is a conceptual WIP and is currently blocked on [a future Trunk feature](https://github.com/thedodd/trunk/issues/46)

There is an alternate multi-thread example [here](https://github.com/yewstack/yew/tree/master/examples/web_worker_fib).


# Multi-Thread Example

[![Demo](https://img.shields.io/website?label=demo&url=https%3A%2F%2Fexamples.yew.rs%2Fmulti_thread)](https://examples.yew.rs/multi_thread)

**WIP**: [thedodd/trunk#46](https://github.com/thedodd/trunk/issues/46)

## Concepts

Expand Down
16 changes: 16 additions & 0 deletions examples/web_worker_fib/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[package]
name = "yew-worker-demo"
voidpumpkin marked this conversation as resolved.
Show resolved Hide resolved
version = "0.1.0"
edition = "2018"
author = "Shrey Somaiya"

[lib]
crate-type = ["cdylib"]

[dependencies]
yew = { path = "../../packages/yew" }
yew-agent = { path = "../../packages/yew-agent" }
wasm-bindgen = "0.2"
js-sys = "0.3"
web-sys = { version = "0.3", features = [ "HtmlInputElement" ] }
serde = "1"
23 changes: 23 additions & 0 deletions examples/web_worker_fib/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Web Worker Demo

Calculate fibbonnaci value of a number in the worker thread,
without blocking the main thread.

You can access a live version here:

## notes
This example is NOT built with [trunk](https://github.com/thedodd/trunk).
Multi-threading in yew does not currently build with Trunk, due to issues described in the [multi_thread](/examples/multi_thread/README.md) example.

Instead the example is built with [`wasm-pack`](https://rustwasm.github.io/wasm-pack/) directly.

To build, run `./build.sh`.
You can then serve the build, with `./serve.sh`.

This example uses python3 as a server, any alternative will work.


# Thanks to

- [insou22](https://github.com/insou22) for writing up the demo.
- [https://github.com/yvt/img2text](https://github.com/yvt/img2text) -- for how to make web workers compile in wasm
8 changes: 8 additions & 0 deletions examples/web_worker_fib/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/usr/bin/env bash
if ! command -v wasm-pack 2>&1 >/dev/null;
then
echo 'error: you must install wasm-pack'
exit
fi

wasm-pack build --target no-modules --out-name wasm --out-dir ./static --no-typescript
9 changes: 9 additions & 0 deletions examples/web_worker_fib/serve.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/usr/bin/env bash
if ! command -v python3 2>&1 >/dev/null;
then
echo 'error: you must install python3'
exit
fi

cd static/
python3 -m http.server
55 changes: 55 additions & 0 deletions examples/web_worker_fib/src/agent.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
use serde::{Deserialize, Serialize};
use yew_agent::{Agent, AgentLink, HandlerId, Public};

pub struct Worker {
link: AgentLink<Self>,
}

#[derive(Serialize, Deserialize)]
pub struct WorkerInput {
pub n: u32,
}

#[derive(Serialize, Deserialize)]
pub struct WorkerOutput {
pub value: u32,
}

impl Agent for Worker {
type Reach = Public<Self>;
type Message = ();
type Input = WorkerInput;
type Output = WorkerOutput;

fn create(link: AgentLink<Self>) -> Self {
Self { link }
}

fn update(&mut self, _msg: Self::Message) {
// no messaging
}

fn handle_input(&mut self, msg: Self::Input, id: HandlerId) {
// this runs in a web worker
// and does not block the main
// browser thread!

let n = msg.n;

fn fib(n: u32) -> u32 {
if n <= 1 {
1
} else {
fib(n - 1) + fib(n - 2)
}
}

let output = Self::Output { value: fib(n) };

self.link.respond(id, output);
}

fn name_of_resource() -> &'static str {
"wasm.js"
}
}
73 changes: 73 additions & 0 deletions examples/web_worker_fib/src/app.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
use crate::agent::{Worker, WorkerInput, WorkerOutput};

use web_sys::HtmlInputElement;
use yew::prelude::*;
use yew_agent::{Bridge, Bridged};

pub struct Model {
clicker_value: u32,
input_ref: NodeRef,
worker: Box<dyn Bridge<Worker>>,
fibonacci_output: String,
}

pub enum Message {
Click,
RunWorker,
WorkerMsg(WorkerOutput),
}

impl Component for Model {
type Message = Message;
type Properties = ();

fn create(ctx: &Context<Self>) -> Self {
let worker = Worker::bridge(ctx.link().callback(Self::Message::WorkerMsg));

Self {
clicker_value: 0,
input_ref: NodeRef::default(),
worker,
fibonacci_output: String::from("Try out some fibonacci calculations!"),
}
}

fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
match msg {
Self::Message::Click => {
self.clicker_value += 1;
}
Self::Message::RunWorker => {
if let Some(input) = self.input_ref.cast::<HtmlInputElement>() {
if let Ok(value) = input.value().parse::<u32>() {
// start the worker off!
self.worker.send(WorkerInput { n: value });
}
voidpumpkin marked this conversation as resolved.
Show resolved Hide resolved
}
}
Self::Message::WorkerMsg(output) => {
// the worker is done!
self.fibonacci_output = format!("Fibonacci value: {}", output.value);
}
}

true
}

fn view(&self, ctx: &Context<Self>) -> Html {
html! {
<>
<h1>{ "Web worker demo" }</h1>
<p>{ "Submit a value to calculate, then increase the counter on the main thread!"} </p>
<p>{ "Large numbers will take some time!" }</p>
<h3>{ "Output: " } { &self.fibonacci_output }</h3>
<br />
<input ref={self.input_ref.clone()} type="number" value="44" max="50"/>
<button onclick={ctx.link().callback(|_| Message::RunWorker)}>{ "submit" }</button>
<br /> <br />
<h3>{ "Main thread value: " } { self.clicker_value }</h3>
<button onclick={ctx.link().callback(|_| Message::Click)}>{ "click!" }</button>
</>
}
}
}
19 changes: 19 additions & 0 deletions examples/web_worker_fib/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#![recursion_limit = "1024"]
#![allow(clippy::large_enum_variant)]

pub mod agent;
pub mod app;
use app::Model;
use wasm_bindgen::prelude::*;
use yew_agent::Threaded;

#[wasm_bindgen(start)]
pub fn start() {
use js_sys::{global, Reflect};

if Reflect::has(&global(), &JsValue::from_str("window")).unwrap() {
yew::start_app::<Model>();
} else {
agent::Worker::register();
}
}
12 changes: 12 additions & 0 deletions examples/web_worker_fib/static/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Yew web worker demo</title>
<script src="wasm.js"></script>
<script type="application/javascript">wasm_bindgen('wasm_bg.wasm');</script>
</head>

<body>
</body>
</html>