About a week ago I got seriously bothered to figure out whether it is possible to run BPXE (a workflow automation engine I am developing) in a browser. I mean, theoretically it was always a possibility (Rust can target
It’s been a frantic week, suffice to say! The problem is that this engine is more than just a library, it’s rather a long-running engine disguised as a library so that it can be precisely customized to do what needs to be done. It uses Rust’s
async a lot. It also has an embedded scripting capability (👋 Rhai!). It has a lot of dependencies.
As a result of my journey, I’ve published a couple of crates under the umbrella of wasm.rs.
First one is really simple. I just wanted a better way to see my debugging output. So I wrote a drop-in replacement for
dbg! macro that works both in the browser and other targets. I do realize that WebAssembly is no longer just for “web” and I’ll see if anything needs to be done about other WebAssembly targets. Nevertheless, please welcome wasm-rs-dbg!
Next one was really important. I’ve tried running BPXE and its own tests on a bunch of async executors (from Tokio,
async-executor, etc.) to no avail. They all kept breaking when I was running them in the browser. Mostly due to unavailability of synchronization primitives in the default standard library. Even though some of these were explicitly “local” (one thread) executors.
So I ended up developing my own executor, which is (at this time, since there’s no good multi-threading support for WebAssembly in Rust) strictly single-threaded. I am not yet 100% confident it’s perfect, but it’s been able to run all of BPXE. In the future, should better support for multi-threading materialize, I’ll be happy to grow it in that direction as well. You can find it in the wasm-rs-async-executor crate.
Lastly, but not least importantly, I had to figure out how to make long-running processes work well. As we all know, the main thread of the browser is not a place to do such things unless you’re doing it with callbacks, or
postMessage between threads and have handlers ready. But I am running a full-time async executor in Rust already. I can’t have an
SharedArrayBuffer (that is currently coming out of limbo caused by the Spectre bug) and
As a result, I developed the first version of wasm-rs-shared-channel which takes advantage of these primitives and gives you a SPSC (single publisher single consumer) channel so that your main thread can send messages to a worker waiting for messages in a thread (which can then
post_message responses back or even use another channel if the other side can poll or wait). Check out the quick-and-dirty example of how it can be used.
There’s more to be done. For instance, I have not found a great way to launch a worker and pass a
Throughout this week I’ve found that it is not trivial to figure the state of things, best practices and ideas when it comes to running Rust on a WebAssembly target. There’s some rigidity in the tools, there’s a lack of explicit, tested support of this target. There’s a general lack of knowledge (I’ve been told that
tokio::sync channel primitives should probably be patched to work; so far I used them straight from the source, and did seem to work — after I had my own executor). Suffice to say, it’s frustrating.
I want to improve the status quo. I want to meet other enthusiasts that are discovering or working on this part of the ecosystem. There’s a “#wg-wasm” channel on Rust’s Discord, but I didn’t get a sense of a community there. It’s a “work group” channel after all.
So, I’ve launched a Discord “server” for wasm.rs — with the express intent for it to become a place to share experience and collaborate on making working with WebAssembly in Rust easy and pleasant.
See you there! I hope this can be of help to others.