-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
d3f88f9
commit 6ddd5cb
Showing
10 changed files
with
281 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
[package] | ||
name = "thread-object-sharing" | ||
version = "0.1.0" | ||
authors = ["The wasm-bindgen Developers"] | ||
edition = "2021" | ||
rust-version = "1.74" | ||
|
||
[lib] | ||
crate-type = ["cdylib"] | ||
|
||
[dependencies] | ||
wasm-bindgen = "0.2.87" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
# Object sharing across threads | ||
|
||
You can build the example locally with: | ||
|
||
``` | ||
$ python3 ./run.py | ||
``` | ||
|
||
and then visiting http://localhost:8080 in a browser should run the example! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
#!/usr/bin/env python3 | ||
import os | ||
import subprocess | ||
|
||
root_dir = os.path.dirname(__file__) | ||
|
||
# A couple of steps are necessary to get this build working which makes it slightly | ||
# nonstandard compared to most other builds. | ||
# | ||
# * First, the Rust standard library needs to be recompiled with atomics | ||
# enabled. to do that we use Cargo's unstable `-Zbuild-std` feature. | ||
# | ||
# * Next we need to compile everything with the `atomics` and `bulk-memory` | ||
# features enabled, ensuring that LLVM will generate atomic instructions, | ||
# shared memory, passive segments, etc. | ||
|
||
os.environ.update( | ||
{"RUSTFLAGS": "-C target-feature=+atomics,+bulk-memory,+mutable-globals"} | ||
) | ||
|
||
subprocess.run( | ||
[ | ||
"cargo", | ||
"build", | ||
"--target", | ||
"wasm32-unknown-unknown", | ||
"--release", | ||
"-Zbuild-std=std,panic_abort", | ||
], | ||
cwd=root_dir, | ||
).check_returncode() | ||
|
||
# Note the usage of `--target no-modules` here which is required for passing | ||
# the memory import to each wasm module. | ||
subprocess.run( | ||
[ | ||
"cargo", | ||
"run", | ||
"-p", | ||
"wasm-bindgen-cli", | ||
"--", | ||
os.path.join( | ||
root_dir, | ||
"..", | ||
"..", | ||
"target", | ||
"wasm32-unknown-unknown", | ||
"release", | ||
"thread_object_sharing.wasm", | ||
), | ||
"--out-dir", | ||
os.path.join(root_dir, "pkg"), | ||
"--target", | ||
"web", | ||
# "--split-linked-modules", | ||
], | ||
cwd=root_dir, | ||
).check_returncode() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
|
||
<head> | ||
<title>Sharing a wasm object across workers</title> | ||
<link rel="icon" href="data:image/x-icon;base64,AA"> | ||
<script type="module" src="index.js"></script> | ||
</head> | ||
|
||
<body> | ||
<button id="toggle-button" disabled>Loading...</button> | ||
<div id="value-display"> | ||
<meter id="value-meter" min="-1000" max="1000">Some atomic value</meter> | ||
<span id="value-display-text">0</span> | ||
</div> | ||
<input id="increment-input" type="range" min="-10" max="10" value="0" /> | ||
<label for="increment-input">Amount to increment per tick</label> | ||
</body> | ||
|
||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
/** @type {import('./pkg/thread_object_sharing')} */ | ||
import initWasm, { OpaqueObject } from "./pkg/thread_object_sharing.js"; | ||
|
||
// Create a few webworkers, leaving some breathing room so our computer doesn't die | ||
const numWorkers = navigator.hardwareConcurrency / 2; | ||
const workers = []; | ||
for (let i = 0; i < numWorkers; ++i) { | ||
workers.push(new Worker( | ||
new URL('./worker.js', import.meta.url), | ||
{ name: 'Call OpaqueObject.get() a bunch of times', type: 'module' }, | ||
)); | ||
} | ||
|
||
// Load the webassembly and send our memory to the workers | ||
const wasmInternals = await initWasm(); | ||
for (const worker of workers) { | ||
worker.postMessage({ type: 'load', memory: wasmInternals.memory }); | ||
} | ||
|
||
const opaqueObject = new OpaqueObject(47); | ||
|
||
// True iff webworkers are running | ||
let running = false; | ||
|
||
// Visual representation of opaqueObject.get() | ||
const valueMeter = window.document.getElementById('value-meter'); | ||
const valueDisplayText = window.document.getElementById('value-display-text'); | ||
|
||
// Change the rate at which the webworkers increment opaqueObject.get()'s value | ||
const incrementInput = window.document.getElementById('increment-input'); | ||
incrementInput.onchange = (event) => { | ||
const incrementVal = event.target.value; | ||
for (const worker of workers) { | ||
worker.postMessage({ type: 'increment', incrementVal }); | ||
} | ||
} | ||
|
||
/** | ||
* Start the webworkers | ||
*/ | ||
function start() { | ||
for (const worker of workers) { | ||
worker.postMessage({ type: 'start', opaqueObjectPtr: opaqueObject.__wbg_ptr }); | ||
} | ||
running = true; | ||
} | ||
|
||
/** | ||
* Stop the webworkers | ||
*/ | ||
function stop() { | ||
for (const worker of workers) { | ||
worker.postMessage({ type: 'stop' }); | ||
} | ||
running = false; | ||
} | ||
|
||
// Clickable button to start and stop the webworkers | ||
const toggleButton = window.document.getElementById('toggle-button'); | ||
toggleButton.onclick = () => { | ||
if (running) { | ||
stop(); | ||
} else { | ||
start(); | ||
} | ||
toggleButton.innerText = running ? 'stop' : 'start'; | ||
}; | ||
toggleButton.removeAttribute('disabled'); | ||
toggleButton.innerText = running ? 'stop' : 'start'; | ||
|
||
function render() { | ||
const val = opaqueObject.get(); | ||
valueMeter.setAttribute('value', val); | ||
valueDisplayText.innerText = val.toString(); | ||
|
||
window.requestAnimationFrame(render); | ||
} | ||
window.requestAnimationFrame(render); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
#!/usr/bin/env python3 | ||
import os | ||
import subprocess | ||
|
||
root_dir = os.path.dirname(__file__) | ||
|
||
subprocess.run(["python3", "build.py"], cwd=root_dir).check_returncode() | ||
|
||
subprocess.run(["python3", "server.py"], cwd=root_dir).check_returncode() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
#!/usr/bin/env python3 | ||
from http.server import HTTPServer, SimpleHTTPRequestHandler, test | ||
import sys | ||
|
||
|
||
class RequestHandler(SimpleHTTPRequestHandler): | ||
def end_headers(self): | ||
self.send_header("Cross-Origin-Opener-Policy", "same-origin") | ||
self.send_header("Cross-Origin-Embedder-Policy", "require-corp") | ||
SimpleHTTPRequestHandler.end_headers(self) | ||
|
||
|
||
if __name__ == "__main__": | ||
test( | ||
RequestHandler, HTTPServer, port=int(sys.argv[1]) if len(sys.argv) > 1 else 8080 | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
use std::sync::atomic::{AtomicI32, Ordering}; | ||
|
||
use wasm_bindgen::prelude::*; | ||
|
||
#[wasm_bindgen] | ||
pub struct OpaqueObject { | ||
val: AtomicI32, | ||
} | ||
|
||
#[wasm_bindgen] | ||
impl OpaqueObject { | ||
#[wasm_bindgen(constructor)] | ||
pub fn new(val: i32) -> Self { | ||
Self { | ||
val: AtomicI32::new(val), | ||
} | ||
} | ||
|
||
#[wasm_bindgen] | ||
pub fn get(&self) -> i32 { | ||
self.val.load(Ordering::SeqCst) | ||
} | ||
|
||
#[wasm_bindgen] | ||
pub fn add(&self, num: i32) -> i32 { | ||
self.val.fetch_add(num, Ordering::SeqCst) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
/** @type {import('./pkg/thread_object_sharing')} */ | ||
import init, { OpaqueObject } from "./pkg/thread_object_sharing.js"; | ||
|
||
/** | ||
* @typedef {{type: 'load', memory: WebAssembly.Memory}} WorkerRequestLoad | ||
* @typedef {{type: 'start', opaqueObjectPtr: number}} WorkerRequestStart | ||
* @typedef {{type: 'stop'}} WorkerRequestStop | ||
* @typedef {{type: 'increment', 'incrementVal': number}} WorkerRequestIncrement | ||
* @typedef {WorkerRequestLoad | WorkerRequestStart | WorkerRequestStop | WorkerRequestIncrement} WorkerRequest | ||
*/ | ||
|
||
/** @type {number | undefined} */ | ||
let intervalTimer = undefined; | ||
let incrementVal = 10; | ||
|
||
/** @type {OpaqueObject} */ | ||
const opaqueObject = Object.create(OpaqueObject.prototype); | ||
|
||
function tick() { | ||
opaqueObject.add(incrementVal); | ||
} | ||
|
||
self.onmessage = async (event) => { | ||
/** | ||
* @type {WorkerRequest} | ||
*/ | ||
const request = event.data; | ||
|
||
switch (request.type) { | ||
case 'load': | ||
await init(undefined, request.memory); | ||
break; | ||
case 'start': | ||
opaqueObject.__wbg_ptr = request.opaqueObjectPtr; | ||
if (intervalTimer === undefined) { | ||
intervalTimer = self.setInterval(tick, 15); | ||
console.log('starting intervalTimer'); | ||
} | ||
break; | ||
case 'stop': | ||
console.log('stopping intervalTimer'); | ||
self.clearInterval(intervalTimer); | ||
intervalTimer = undefined; | ||
break; | ||
case 'increment': | ||
incrementVal = request.incrementVal; | ||
console.log(`New increment value: ${incrementVal}`); | ||
break; | ||
} | ||
}; |