ultimate-webgpu / browseml-thread.js
Gregory-L's picture
ultimate WebGPU: Agate and Qwen3 on the GPU, bankML separate on the CPU, in the ultimate input field
65bce6f verified
Raw History Blame Contribute Delete
1.75 kB
// SPDX-License-Identifier: MIT OR Apache-2.0
// One thread of bankML's engine in the browser (browseml-mt.wasm). The engine worker creates these before the engine
// starts — a thread that is waiting cannot start a worker — and hands each one a thread when the engine's pool
// asks (wasi.thread-spawn). Each instantiates the same module on the same shared memory, then runs
// wasi_thread_start(tid, start_arg), which runs the pool's worker loop until the engine is closed.
// engine → thread {type: "init", module, memory} then {type: "start", tid, arg}
// thread → engine {type: "ready"} · {type: "log", level, text} · {type: "exit", tid, error?}
import { wasi } from "./browseml-wasi.js";
let instance = null;
onmessage = async (e) => {
const m = e.data;
if (m.type === "init") {
// no files here: the threads only compute; standard error goes to the page's log
const w = wasi({ files: {}, write: (fd, s) => postMessage({ type: "log", level: 1, text: s.trimEnd() }) });
instance = await WebAssembly.instantiate(m.module, {
...w.imports,
env: { memory: m.memory },
wasi: { "thread-spawn": () => -1 }, // the engine's threads do not start threads of their own
browseml: { piece: () => 1, log: (level, p, n) => postMessage({ type: "log", level, text: "(thread) " + new TextDecoder().decode(new Uint8Array(m.memory.buffer).slice(p, p + n)) }) },
});
w.bind(m.memory);
postMessage({ type: "ready" });
} else if (m.type === "start") {
try {
instance.exports.wasi_thread_start(m.tid, m.arg);
postMessage({ type: "exit", tid: m.tid });
} catch (err) {
postMessage({ type: "exit", tid: m.tid, error: String(err && err.message || err) });
}
}
};