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

frontend: use single wasm instance #4

Merged
merged 1 commit into from
Nov 15, 2024
Merged
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
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ jobs:

- name: test
run: |
$CROSS_EMULATOR dist/pandoc.wasm pandoc/README.md -o pandoc/README.rst
wasmtime run --dir $PWD::/ -- dist/pandoc.wasm pandoc/README.md -o pandoc/README.rst
head --lines=20 pandoc/README.rst

- name: upload-pages-artifact
Expand Down
9 changes: 3 additions & 6 deletions frontend/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -67,16 +67,13 @@
<input type="text" id="arguments" value="-f markdown -t rst" />
</div>
<script type="module">
import { run_pandoc } from "./index.js";
import { pandoc } from "./index.js";

async function updateOutput() {
const inputText = document.getElementById("input").value;
const argumentsText = document
.getElementById("arguments")
.value.trim()
.split(" ");
const argumentsText = document.getElementById("arguments").value;
try {
const output = await run_pandoc(argumentsText, inputText);
const output = pandoc(argumentsText, inputText);
document.getElementById("output").value = output;
} catch (err) {
document.getElementById("output").value = `Error: ${err.message}`;
Expand Down
99 changes: 51 additions & 48 deletions frontend/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,60 +3,63 @@ import {
OpenFile,
File,
ConsoleStdout,
PreopenDirectory,
} from "https://cdn.jsdelivr.net/npm/@bjorn3/[email protected]/dist/index.js";

const mod = await WebAssembly.compileStreaming(fetch("./pandoc.wasm"));

const instance_promise_pool_size = 8;
const args = ["pandoc.wasm", "+RTS", "-H64m", "-RTS"];
const env = [];
const in_file = new File(new Uint8Array(), { readonly: true });
const out_file = new File(new Uint8Array(), { readonly: false });
const fds = [
new OpenFile(new File(new Uint8Array(), { readonly: true })),
ConsoleStdout.lineBuffered((msg) => console.log(`[WASI stdout] ${msg}`)),
ConsoleStdout.lineBuffered((msg) => console.warn(`[WASI stderr] ${msg}`)),
new PreopenDirectory("/", [
["in", in_file],
["out", out_file],
]),
];
const options = { debug: false };
const wasi = new WASI(args, env, fds, options);
const { instance } = await WebAssembly.instantiateStreaming(
fetch("./pandoc.wasm"),
{
wasi_snapshot_preview1: wasi.wasiImport,
}
);

const instance_promise_pool = [];
wasi.initialize(instance);
instance.exports.__wasm_call_ctors();

function instance_promise_pool_fill() {
if (instance_promise_pool.length < instance_promise_pool_size) {
for (
let i = instance_promise_pool.length;
i < instance_promise_pool_size;
++i
) {
const args = [];
const env = [];
const stdin_file = new File(new Uint8Array(), { readonly: true });
const stdout_file = new File(new Uint8Array(), { readonly: false });
const fds = [
new OpenFile(stdin_file),
new OpenFile(stdout_file),
ConsoleStdout.lineBuffered((msg) =>
console.warn(`[WASI stderr] ${msg}`)
),
];
const options = { debug: false };
const wasi = new WASI(args, env, fds, options);
instance_promise_pool.push(
WebAssembly.instantiate(mod, {
wasi_snapshot_preview1: wasi.wasiImport,
}).then((instance) => ({ instance, wasi, stdin_file, stdout_file }))
);
}
}
function memory_data_view() {
return new DataView(instance.exports.memory.buffer);
}

instance_promise_pool_fill();
const argc_ptr = instance.exports.malloc(4);
memory_data_view().setUint32(argc_ptr, args.length, true);
const argv = instance.exports.malloc(4 * (args.length + 1));
for (let i = 0; i < args.length; ++i) {
const arg = instance.exports.malloc(args[i].length + 1);
new TextEncoder().encodeInto(
args[i],
new Uint8Array(instance.exports.memory.buffer, arg, args[i].length)
);
memory_data_view().setUint8(arg + args[i].length, 0);
memory_data_view().setUint32(argv + 4 * i, arg, true);
}
memory_data_view().setUint32(argv + 4 * args.length, 0, true);
const argv_ptr = instance.exports.malloc(4);
memory_data_view().setUint32(argv_ptr, argv, true);

const instances = (async function* () {
while (true) {
yield await instance_promise_pool.shift();
instance_promise_pool_fill();
}
})();
instance.exports.hs_init_with_rtsopts(argc_ptr, argv_ptr);

export async function run_pandoc(args, stdin_str) {
const { instance, wasi, stdin_file, stdout_file } = (await instances.next())
.value;
wasi.args = ["pandoc.wasm", ...args];
stdin_file.data = new TextEncoder().encode(stdin_str);
const ec = wasi.start(instance);
if (ec !== 0) {
throw new Error(`Non-zero exit code ${ec}`);
}
return new TextDecoder("utf-8", { fatal: true }).decode(stdout_file.data);
export function pandoc(args_str, in_str) {
const args_ptr = instance.exports.malloc(args_str.length);
new TextEncoder().encodeInto(
args_str,
new Uint8Array(instance.exports.memory.buffer, args_ptr, args_str.length)
);
in_file.data = new TextEncoder().encode(in_str);
instance.exports.wasm_main(args_ptr, args_str.length);
return new TextDecoder("utf-8", { fatal: true }).decode(out_file.data);
}
Loading