wasi-compute
explores the minimal viable model for fully deterministic
computations/functions in WASI. The explored model is a direct result
of discussions found in WebAssembly/WASI/issues/190 thread.
This repo was partly done in preparation for my talk about determinism in WASI in distributed settings at FOSDEM2020. Kudos to everyone who contributed and to @sunfishcode in particular for his infinite wisdom!
The model is pretty simple in its assumptions. The "compute" function takes in two
arguments: an input WASI file descriptor, in
, and an output WASI file descriptor,
out
. Thanks to the capabilities-based security model of WASI, we are able to enforce
in
to only have rights::fd_read
rights attached to it, and out
to only have
rights::fd_write
rights. This is handled at the preopening stage of the resources
in the runtime proper (in this case, the modified version of wasmtime
;
see Running the examples using wasmtime
section).
Constraining the descriptors to reading from or writing to immediately shields us from any nondeterminism that might arise from utilising any syscalls operating on raw file descriptors or paths in WASI. In fact, the only permitted syscalls are
Of course, other syscalls not requiring file descriptors as arguments such as
random_get
or clock_time_get
can still be invoked in this model; however,
with the upcoming WASI snapshot (see ephemeral stage in WASI repo), these syscalls
will get their own WASI modules, and hence they will require a capability to be
available at run time. Here, in order to emulate this, I've gone ahead and filtered
out those offending syscalls manually in my tweaked version of wasmtime
(see
Running the examples using wasmtime
).
- Wat:
(func (export "compute") (param $in u32) (param $out u32))
- Rust:
#[no_mangle]
pub extern "C" fn compute(r#in: wasi::Fd, out: wasi::Fd);
- C/C++:
void compute(__wasi_fd_t in, __wasi_fd_t out);
In order to build the examples contained within this repo, you'll need two things:
- latest version of Rust nightly (I strongly recommend using rustup to manage your Rust
installation) -- stable and beta channels are fine as well however, at the time of writing,
they do not guarantee you'll be using the latest WASI ABI snapshot, aka
snapshot1
cargo-wasi
plugin tocargo
The steps are thus as follows:
rustup toolchain add nightly
cargo install cargo-wasi
cargo +nightly wasi build --release
When executed from the root of this repo, these 3 steps will build all examples targetting
the wasm32-wasi
target in release mode. Note that you don't have to install the target
yourself using rustup
as cargo-wasi
will do it for you when you build the project for
the first time.
All of the examples contained within this repo require a tweaked version
of the wasmtime
runtime which can be found in my fork kubkon/wasmtime/tree/preopen_fd.
Therefore, in order to run the examples, you'll need to clone the repo and build it using the
latest version of Rust:
git clone https://github.com/kubkon/wasmtime
cd wasmtime
git checkout preopen_fd
cargo build --release
The tweaked version of the runtime adds two optional arguments to wasmtime
which are
required in order to map a WASI file descriptor to the preopened resource on the host.
There are:
-
--preopen_read=GUEST_FD:PATH_TO_PREOPEN
which will preopen and map a resource opened for reading only, or in WASI terms, with rights set torights::fd_read
-
--preopen_write=GUEST_FD:PATH_TO_PREOPEN
which will preopen and map a resource opened for writing only, or in WASI terms, with rights set torights::fd_write
The examples and any code conforming to the discussed model can then be run using
the following wasmtime
invocation:
wasmtime --preopen_read=READ_FD:PATH_TO_PREOPEN \
--preopen_write=WRITE_FD:PATH_TO_PREOPEN \
--invoke=compute \
example.wasm -- READ_FD WRITE_FD
Fully working examples demonstrating the viability of the proposed approach can be found in this repo, and are as follows:
-
hello-compute - demonstrates a minimal "Hello World!"-style function which reads from the input WASI file descriptor, makes whatever it read upper-case, and writes the result to the output WASI file descriptor
-
test-compute - verifies that only reading from/writing to a specified, preopened WASI file descriptor are possible
-
flite-compute - demonstrates that it is already possible to fit a full-fledged library into this model by taking a text-to-speech flite engine and performing simple TTS on the input WASI file descriptor and saving the result to the output WASI file descriptor
All of the examples presented here are highly experimental in nature and should not be relied upon in any shape or form, and by using them you agree to use them at your own risk!