Skip to content

Commit e9cf712

Browse files
authored
Merge pull request AssemblyScript#3 from 01alchemist/feature/worker-based-threads-wait-wake-integration
Feature/worker based threads wait wake integration
2 parents ea23fe0 + cbaa786 commit e9cf712

File tree

12 files changed

+439
-6
lines changed

12 files changed

+439
-6
lines changed
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
const fs = require("fs");
2+
const path = require("path");
3+
4+
const compiled = new WebAssembly.Module(
5+
fs.readFileSync(path.resolve(__dirname, "..", "build", "atomic.builtins.wasm"))
6+
);
7+
const memory = new WebAssembly.Memory({
8+
initial: 256,
9+
maximum: 256,
10+
shared: true
11+
})
12+
const imports = {
13+
env: {
14+
memory,
15+
abort: (filename, line, column) => {
16+
throw Error("abort called at " + line + ":" + colum);
17+
}
18+
}
19+
};
20+
21+
Object.defineProperty(module, "exports", {
22+
get: () => new WebAssembly.Instance(compiled, imports).exports
23+
});
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import "allocator/atomic";
2+
import { itoa } from "internal/number";
3+
4+
declare function log_str(v: string): void;
5+
6+
var id = 1;
7+
type int = i32;
8+
// type int = i64;
9+
10+
export function setId(_id: i32): void {
11+
id = _id;
12+
}
13+
14+
export function wait(mutexAddr: i32 = 0, value: int = 1): void {
15+
log_str('[WASM][' + itoa<i32>(id) + '] waiting...');
16+
Atomic.store<int>(mutexAddr, value);
17+
Atomic.wait<int>(mutexAddr, value, -1);
18+
log_str('[WASM][' + itoa<i32>(id) + '] waken');
19+
}
20+
21+
export function wake(mutexAddr: i32 = 0, value: int = 0, numAgents: i32 = 1): void {
22+
log_str('[WASM][' + itoa<i32>(id) + '] waking '+ itoa<i32>(numAgents) + ' agent(s)...');
23+
Atomic.store<int>(mutexAddr, value);
24+
var count = Atomic.wake<int>(mutexAddr, numAgents);
25+
log_str('[WASM][' + itoa<i32>(id) + '] waken ' + itoa<i32>(count) + ' agent(s)');
26+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"extends": "../../../std/assembly.json",
3+
"include": [
4+
"./**/*.ts"
5+
]
6+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
"use strict";
2+
Object.defineProperty(exports, "__esModule", { value: true });
3+
require("allocator/arena");
4+
let str = "A";
5+
function read() {
6+
return str;
7+
}
8+
exports.read = read;

examples/atomic-wait-wake/index.html

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<title>Atomic wait wake - AssemblyScript</title>
5+
<link rel="icon" href="http://assemblyscript.org/favicon.ico" type="image/x-icon" />
6+
<meta name="viewport" content="user-scalable=0" />
7+
<style>
8+
html,
9+
body {
10+
height: 100%;
11+
margin: 0;
12+
overflow: hidden;
13+
color: #111;
14+
background: #fff;
15+
font-family: sans-serif;
16+
}
17+
18+
body {
19+
border-top: 2px solid #070809;
20+
}
21+
22+
h1 {
23+
padding: 18px 20px 20px;
24+
font-size: 12pt;
25+
margin: 0;
26+
}
27+
28+
a {
29+
color: #111;
30+
text-decoration: none;
31+
}
32+
33+
a:hover {
34+
color: #efbd03;
35+
text-decoration: underline;
36+
}
37+
38+
canvas {
39+
position: absolute;
40+
top: 60px;
41+
left: 20px;
42+
width: calc(100% - 40px);
43+
height: calc(100% - 80px);
44+
background: #070809;
45+
}
46+
.info {
47+
padding: 2rem;
48+
}
49+
</style>
50+
</head>
51+
52+
<body>
53+
<h1>
54+
<a href="https://github.com/WebAssembly/threads">Atomic wait wake</a> in
55+
<a href="http://assemblyscript.org">AssemblyScript</a> (
56+
<a
57+
href="https://github.com/AssemblyScript/assemblyscript/blob/master/examples/shared-memory/assembly/index.ts"
58+
>source</a
59+
>
60+
)
61+
<br>
62+
</h1>
63+
<div class="info">Open console to results</div>
64+
<script>
65+
"use strict";
66+
67+
const memory = new WebAssembly.Memory({
68+
initial: 256,
69+
shared: true,
70+
maximum: 256,
71+
});
72+
const memoryView = new DataView(memory.buffer);
73+
let _exports = {};
74+
const workers = [];
75+
async function init() {
76+
const res = await fetch("build/optimized.wasm");
77+
const buffer = await res.arrayBuffer();
78+
const wasm = await WebAssembly.compile(buffer);
79+
let worker1 = new Worker("./js/worker1.js");
80+
let worker2 = new Worker("./js/worker1.js");
81+
worker1.onmessage = handleMessage;
82+
worker2.onmessage = handleMessage;
83+
workers.push(worker1)
84+
workers.push(worker2);
85+
worker1.postMessage({ command: "init", id:1, memory, wasm });
86+
worker2.postMessage({ command: "init", id:2, memory, wasm });
87+
}
88+
let readyCount = 0;
89+
function handleMessage(event) {
90+
switch(event.data.command){
91+
case "inited":{
92+
readyCount++;
93+
if(readyCount === 2) {
94+
workers[0].postMessage({ command: "wait", value: 1 });
95+
setTimeout(() => {
96+
workers[1].postMessage({ command: "wake", value: 1 });
97+
}, 1000)
98+
99+
setTimeout(() => {
100+
workers[0].postMessage({ command: "wait_js", value: 1 });
101+
}, 1500)
102+
setTimeout(() => {
103+
workers[1].postMessage({ command: "wake", value: 2 });
104+
}, 3000)
105+
// setTimeout(() => {
106+
// workers[0].postMessage({ command: "wait_i64", value: 1 });
107+
// }, 0)
108+
// setTimeout(() => {
109+
// workers[1].postMessage({ command: "wake_i64", value: 2 });
110+
// }, 1000)
111+
setTimeout(() => {
112+
workers[0].postMessage({ command: "wait", value: 1 });
113+
}, 3500)
114+
setTimeout(() => {
115+
workers[1].postMessage({ command: "wake_js", value: 2 });
116+
}, 5000)
117+
}
118+
break;
119+
}
120+
}
121+
}
122+
init();
123+
</script>
124+
</body>
125+
</html>
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
addEventListener("message", onMessageReceived, false);
2+
let memory = null;
3+
let dataView = null;
4+
let u8 = null;
5+
let i32 = null;
6+
let id = 0;
7+
let _exports = null;
8+
const mutexAddr = 0;
9+
const numAgents = 1;
10+
async function onMessageReceived(e) {
11+
try {
12+
const data = e.data;
13+
switch (data.command) {
14+
case "init": {
15+
id = data.id;
16+
memory = data.memory;
17+
dataView = new DataView(memory.buffer);
18+
u8 = new Uint8Array(memory.buffer);
19+
i32 = new Int32Array(memory.buffer);
20+
const instance = await WebAssembly.instantiate(data.wasm, {
21+
env: {
22+
memory,
23+
abort: function() {},
24+
},
25+
index: {
26+
log: console.log,
27+
log_str,
28+
},
29+
});
30+
const exp = instance.exports;
31+
32+
_exports = exp;
33+
_exports.setId(id);
34+
self.postMessage({ command: "inited" });
35+
break;
36+
}
37+
case "wait": {
38+
_exports.wait(mutexAddr, data.value);
39+
break;
40+
}
41+
case "wait_i64": {
42+
_exports.wait_i64(mutexAddr, data.value);
43+
break;
44+
}
45+
case "wait_js": {
46+
console.log(`[JS][${id}] waiting...`)
47+
Atomics.store(i32, mutexAddr, data.value);
48+
Atomics.wait(i32, mutexAddr, data.value);
49+
console.log(`[JS][${id}] waken`);
50+
break;
51+
}
52+
case "wake": {
53+
_exports.wake(mutexAddr, data.value, numAgents);
54+
console.log('-----')
55+
break;
56+
}
57+
case "wake_i64": {
58+
_exports.wake_i64(mutexAddr, data.value, numAgents);
59+
console.log('-----')
60+
break;
61+
}
62+
case "wake_js": {
63+
console.log(`[JS][${id}] waking ${numAgents} agent(s)...`)
64+
Atomics.store(i32, mutexAddr, data.value);
65+
const count = Atomics.wake(i32, mutexAddr, numAgents);
66+
console.log(`[JS][${id}] waken ${count} agent(s)`)
67+
console.log('-----')
68+
break;
69+
}
70+
}
71+
} catch (e) {
72+
console.log(e);
73+
}
74+
}
75+
76+
function log_str(ptr) {
77+
console.log(readUTF16(ptr, dataView));
78+
}
79+
80+
function readUTF16(ptr, _memoryView) {
81+
_memoryView = _memoryView || memoryView;
82+
const u8a = new Uint8Array(_memoryView.buffer);
83+
const str_len = _memoryView.getUint32(ptr, true);
84+
const utf16 = u8a.subarray(ptr + 4, ptr + (str_len * 2) + 4);
85+
const decoder = new TextDecoder("utf-16");
86+
const _utf16 = utf16.map(a => a);
87+
return decoder.decode(_utf16);
88+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
addEventListener("message", onMessageReceived, false);
2+
let memory = null;
3+
let i32 = null;
4+
function onMessageReceived(e) {
5+
try {
6+
const data = e.data;
7+
switch (data.command) {
8+
case "init": {
9+
memory = data.memory;
10+
i32 = new Int32Array(memory.buffer);
11+
setTimeout(() => {
12+
Atomics.store(i32, 0, 123);
13+
console.log("Stored", 123);
14+
Atomics.wake(i32, 0, 1);
15+
}, 2000);
16+
}
17+
}
18+
} catch (e) {}
19+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"name": "@assemblyscript/shared-memory-example",
3+
"version": "1.0.0",
4+
"private": true,
5+
"scripts": {
6+
"asbuild:untouched": "asc assembly/index.ts -b build/untouched.wasm -t build/untouched.wat --sourceMap --importMemory --sharedMemory=256 --validate",
7+
"asbuild:optimized": "asc assembly/index.ts -b build/optimized.wasm -t build/optimized.wat -O3 --importMemory --sharedMemory=256 --validate --noAssert",
8+
"asbuild": "npm run asbuild:untouched && npm run asbuild:optimized",
9+
"build": "npm run asbuild",
10+
"server": "http-server . -o -c-1",
11+
"test": "node tests"
12+
},
13+
"devDependencies": {
14+
"http-server": "^0.11.1"
15+
}
16+
}

0 commit comments

Comments
 (0)