bytecodealliance / lucet

Lucet, the Sandboxing WebAssembly Compiler.
Apache License 2.0
4.07k stars 164 forks source link

Support async in lucet-wiggle, and switch lucet-wasi to use tokio #655

Closed pchickey closed 3 years ago

pchickey commented 3 years ago

tldr

Support async in lucet-wiggle

Earlier this year, support for async in wiggle landed in upstream wasmtime in https://github.com/bytecodealliance/wasmtime/pull/2701, and using this async functionality so that wasi-common can operate on tokio landed in https://github.com/bytecodealliance/wasmtime/pull/2832.

This PR ports Lucet to use those new capabilities of wiggle and wasi-common, where the entire host function is an async fn executed on the host stack via Vmctx::block_on. This is a significant departure from the existing Lucet async style of only using Vmctx::block_on in small sections. However, it is also currently impossible to use the Wiggle module traits as defined by upstream wasi-common, because currently Wasmtime does not require async host functions to impl Send, and in fact they close over WasiCtx which is not safe to Send.

To solve this impedance mismatch, this PR vendors in the changes made to the wiggle and wasi-common family of crates by @alexcrichton in https://github.com/bytecodealliance/wasmtime/pull/2897. These changes transform Wiggle module traits to take &mut self rather than &self, and simplify the structure of WasiCtx so that it does now impl Send. So, this version of upstream is compatible with Lucet, but since it is gated behind RFC11 we are vendoring it until it lands. All references to the wiggle{-} and wasi- crates in the Lucet workspace have been changed to paths in the wasmtime-new-api directory.

lucet-wiggle was previously a bit clunky due to constraints in wiggle which went away a while ago. It has been redesigned to work just like wasmtime-wiggle: it re-exports wiggle, and provides its own lucet_integration! proc macro which only defines the code which hooks up the code generated from wiggle::from_witx! to the lucet runtime. Previously lucet-wiggle shipped its own from_witx! macro which combined both of those macros into one.

That change also meant we can get rid of lucet-wasi-generate entirely! No more special proc macro or boilerplate is required for hooking up the wasi-common definitions directly as Lucet hostcalls - just a single lucet_wiggle::lucet_integration! invocation.

Switch lucet-wasi to use tokio

In https://github.com/bytecodealliance/wasmtime/pull/2832 I went to lengths to make give wasmtime-wasi users the option of using plain-old synchronous Rust, or using async Rust on top of tokio. I decided not to do this on Lucet and instead mandate that lucet-wasi users use tokio. My reasons for this are:

  1. Lucet makes it harder to have multiple definitions of import functions than Wasmtime does - you'd have to select whether to use the sync or async tokio definition of a hostcall at AOT compile time via bindings.json
  2. Many production users of Lucet are already running it on top of tokio.
  3. Lucet is approaching EOL - users who need flexibility should switch to wasmtime. All of the features which make wasmtime performance meet or exceed Lucet have landed.

By switching lucet-wasi to use tokio, blocking calls (notably poll_oneoff) interact with the tokio executor correctly - a poll_oneoff waiting on a timer will be implemented by tokio's Timeout future. Other blocking syscalls will use block_in_place to cooperate with the executor. Prior to this change, running lucet-wasi on top of tokio would block the thread without the executor's cooperation, which will almost certainly result in bad behavior.