refraction-networking / water

WebAssembly Transport Executables Runtime
Apache License 2.0
25 stars 1 forks source link

⭐ new: pure Go implementation of water #18

Closed gaukas closed 8 months ago

gaukas commented 8 months ago

As planned, we move away from CGO-based wasmtime-go. This pull request replaces all existing implementations with pure Go implementation without introducing new major components or version/standards. In other words, this PR covers v0-related implementations only, with v1 still WIP (and will be in pure Go as well).

This pull request also solves a few problems we encountered before, #8 and #12.

Acknowledgment

We thank members from @tetratelabs, especially @evacchi, for the huge guidance and assistance we received and the very pleasant communication experience when integrating water with wazero.

gaukas commented 8 months ago

As a side note: still water will not fully work on Windows, given some of the async operations are not really supported by Go runtime on Windows.

gaukas commented 8 months ago

Observation shows the hard-coded setNonblock could be potentially unnecessary or redundant: wazero as the WASM+WASI Runtime is capable of correctly propagating the set_nonblocking calls on a fd via wasi_snapshot_preview1.fd_fdstat_set_flags().

erikziyunchi commented 8 months ago

Observation shows the hard-coded setNonblock could be potentially unnecessary or redundant

Will the redundant call to set_nonblocking be a large overhead or lead to unwanted behavior?

I'm thinking if we still want the compatibility feature as the same .wasm binary to be executed by both the go and rust side, we might still need the call for the rust side? Or we can maintain another set of examples of WASM with better compatibility.

gaukas commented 8 months ago

TL;DR: There shouldn't be a large overhead, but bug risks are possible. In terms of compatibility, it is irrelevant (for Go).


First of all, at least for Go, SetNonblocking is done by invoking a syscall(), which is considered efficient. However, if a WebAssembly instance in any case needs explicitly blocking I/O and we by default set all sockets to non-blocking mode from runtime library, the fd read (done by a syscall() managed by wazero may return async errors such as EAGAIN which do not make much sense.

And also, since this change is exclusive to WATER Go's runtime library without breaking any compatibility promises, this has nothing to do with on Rust's side. To align with our design, in WebAssembly, despite of the source language, it is considered a responsibility of the WebAssembly Transport Module to set the fd into non-blocking mode before assuming the async-based I/O is used. Per WASI specification, this will be done via an WASI API wasi_snapshot_preview1::fd_fdstat_set_flags (link).

Just to clarify, this is a low-level WASI API to be called, not something a developer using a high-level language such as Go or Rust should be aware of -- it must be handled by the compiler when target is set to WASI P1 if full support to WASI P1 is provided. Developer of high-level languages should set non-blocking mode as they normally would, via syscall.SetNonblock() (pkg.go.dev) and set_nonblocking (doc.rust-lang.org).

For the same reason, if it is still unconfirmed, WATER Rust runtime library SHOULD NOT hard-code the set_nonblocking call on every single socket pushed into a WATM -- at least that is what I believed. This hypothesis may need further tests to verify.