Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,13 @@ jobs:
if: matrix.os == 'ubuntu-latest'
run: ./odin check examples/all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:openbsd_amd64

- name: Run shared-memory WASM fixture
if: matrix.os == 'ubuntu-latest'
run: |
./odin build tests/wasm_threads_fixture -target:js_wasm32 -target-features:atomics -out:wasm_threads_fixture.wasm -extra-linker-flags:"--import-memory --shared-memory --initial-memory=16777216 --max-memory=67108864 --export=__stack_pointer"
node tests/wasm_threads_fixture/test.mjs wasm_threads_fixture.wasm
rm wasm_threads_fixture.wasm

- name: Odin check examples/all for js_wasm32
if: matrix.os == 'ubuntu-latest'
run: ./odin check examples/all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -no-entry-point -target:js_wasm32
Expand Down
39 changes: 39 additions & 0 deletions tests/wasm_threads_fixture/main.odin
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package main

import "base:intrinsics"

shared_value: u32 = 41
initialized_value: u32 = 73

@(export, link_name = "fixture_shared_value_ptr")
shared_value_ptr :: proc "contextless" () -> ^u32 {
return &shared_value
}

@(export, link_name = "fixture_initialized_value_ptr")
initialized_value_ptr :: proc "contextless" () -> ^u32 {
return &initialized_value
}

@(export, link_name = "fixture_increment")
fixture_increment :: proc "contextless" () -> u32 {
return intrinsics.atomic_add_explicit(&shared_value, 1, .Seq_Cst) + 1
}

@(export, link_name = "fixture_dispatch")
fixture_dispatch :: proc "contextless" (callback_pointer, context_pointer: rawptr) {
callback := transmute(proc "c" (_: rawptr))callback_pointer
callback(context_pointer)
}

fixture_callback :: proc "c" (context_pointer: rawptr) {
value := (^u32)(context_pointer)
_ = intrinsics.atomic_add_explicit(value, 1, .Seq_Cst)
}

@(export, link_name = "fixture_callback_pointer")
fixture_callback_pointer :: proc "contextless" () -> rawptr {
return rawptr(fixture_callback)
}

main :: proc() {}
71 changes: 71 additions & 0 deletions tests/wasm_threads_fixture/test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import assert from "node:assert/strict";
import fs from "node:fs";

const bytes = fs.readFileSync(process.argv[2]);
const module = await WebAssembly.compile(bytes);
const imports = WebAssembly.Module.imports(module);
const memoryImport = imports.find((entry) => entry.kind === "memory");
assert.deepEqual(memoryImport, { module: "env", name: "memory", kind: "memory" });

const memory = new WebAssembly.Memory({ initial: 256, maximum: 1024, shared: true });
const defaultImports = {
memory,
};
const odinImports = {
write() {},
trap() { throw new Error("WASM trap"); },
alert() {},
abort() { throw new Error("WASM abort"); },
evaluate() {},
open() {},
time_now() { return 0n; },
tick_now() { return 0; },
time_sleep() {},
sqrt: Math.sqrt,
sin: Math.sin,
cos: Math.cos,
pow: Math.pow,
fmuladd: (x, y, z) => x * y + z,
ln: Math.log,
exp: Math.exp,
ldexp: (x, exponent) => x * (2 ** exponent),
rand_bytes() {},
};
const makeInstance = async () => WebAssembly.instantiate(module, {
env: defaultImports,
odin_env: odinImports,
});

const first = await makeInstance();
const second = await makeInstance();
assert.notEqual(first.exports.__stack_pointer, second.exports.__stack_pointer);
first.exports.__stack_pointer.value = 2 * 1024 * 1024;
second.exports.__stack_pointer.value = 3 * 1024 * 1024;
assert.equal(first.exports.__stack_pointer.value, 2 * 1024 * 1024);
assert.equal(second.exports.__stack_pointer.value, 3 * 1024 * 1024);

const sharedPointer = first.exports.fixture_shared_value_ptr();
assert.equal(sharedPointer, second.exports.fixture_shared_value_ptr());
assert.equal(new Uint32Array(memory.buffer, sharedPointer, 1)[0], 41);
assert.equal(first.exports.fixture_increment(), 42);
assert.equal(second.exports.fixture_increment(), 43);

const initializedPointer = first.exports.fixture_initialized_value_ptr();
new Uint32Array(memory.buffer, initializedPointer, 1)[0] = 99;
const third = await makeInstance();
assert.equal(third.exports.fixture_initialized_value_ptr(), initializedPointer);
assert.equal(new Uint32Array(memory.buffer, initializedPointer, 1)[0], 99);

const callbackPointer = first.exports.fixture_callback_pointer();
first.exports.fixture_dispatch(callbackPointer, sharedPointer);
assert.equal(new Uint32Array(memory.buffer, sharedPointer, 1)[0], 44);
second.exports.fixture_dispatch(callbackPointer, sharedPointer);
assert.equal(new Uint32Array(memory.buffer, sharedPointer, 1)[0], 45);

console.log(JSON.stringify({
instances: 3,
sharedValue: 45,
initializedValue: 99,
stacks: [first.exports.__stack_pointer.value, second.exports.__stack_pointer.value],
imports,
}, null, 2));
Loading