Change-Id: Id140b87d77accf36be641c34c1692cb245b87608
Reviewed-on: https://go-review.googlesource.com/c/website/+/508895
Reviewed-by: Cherry Mui <cherryyz@google.com>
Reviewed-by: Eli Bendersky <eliben@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Johan Brandhorst-Satzkorn <johan.brandhorst@gmail.com>
This commit is contained in:
Johan Brandhorst-Satzkorn 2023-07-11 11:44:36 -07:00
Родитель 2918f44bc2
Коммит feb9e77f0d
1 изменённых файлов: 187 добавлений и 0 удалений

187
_content/blog/wasi.md Normal file
Просмотреть файл

@ -0,0 +1,187 @@
---
title: WASI support in Go
date: 2023-09-13
by:
- Johan Brandhorst-Satzkorn, Julien Fabre, Damian Gryski, Evan Phoenix, and Achille Roussel
summary: Go 1.21 adds a new port targeting the WASI preview 1 syscall API
---
Go 1.21 adds a new port targeting the WASI preview 1 syscall API through the
new `GOOS` value `wasip1`. This port builds on the existing WebAssembly
port introduced in Go 1.11.
## What is WebAssembly?
[WebAssembly (Wasm)](https://webassembly.org/) is a binary instruction format
originally designed for the web. It represents a standard that allows
developers to run high-performance, low-level code directly in web browsers at
near-native speeds.
Go first added support for compiling to Wasm in the 1.11 release, through the
`js/wasm` port. This allowed Go code compiled using the Go compiler to be
executed in web browsers, but it required a JavaScript execution environment.
As the use of Wasm has grown, so have use cases outside of the browser. Many
cloud providers are now offering services that allow the user to execute Wasm
executables directly, leveraging the new
[WebAssembly System Interface (WASI)](https://wasi.dev/) syscall API.
## The WebAssembly System Interface
WASI defines a syscall API for Wasm executables, allowing them to interact with
system resources such as the filesystem, the system clock, random data
utilities, and more. The latest release of the WASI spec is called
`wasi_snapshot_preview1`, from which we derive the `GOOS` name `wasip1`. New
versions of the API are being developed, and supporting them in the Go
compiler in the future will likely mean adding a new `GOOS`.
The creation of WASI has allowed a number of Wasm runtimes (hosts) to
standardize their syscall API around it. Examples of Wasm/WASI hosts include
[Wasmtime](https://wasmtime.dev), [Wazero](https://wazero.io/),
[WasmEdge](https://wasmedge.org/), [Wasmer](https://wasmer.io/), and
[NodeJS](https://nodejs.org). There are also a number of cloud providers
offering hosting of Wasm/WASI executables.
## How can we use it with Go?
Make sure that you have installed at least version 1.21 of Go. For this demo,
well use [the Wasmtime host](https://docs.wasmtime.dev/cli-install.html) to
execute our binary. Lets start with a simple `main.go`:
```go
package main
import "fmt"
func main() {
fmt.Println("Hello world!")
}
```
We can build it for `wasip1` using the command:
```shell
$ GOOS=wasip1 GOARCH=wasm go build -o main.wasm main.go
```
This will produce a file, `main.wasm` which we can execute with `wasmtime`:
```shell
$ wasmtime main.wasm
Hello world!
```
Thats all it takes to get started with Wasm/WASI! You can expect almost all
the features of Go to just work with `wasip1`. To learn more about the details
of how WASI works with Go, please see
[the proposal](https://go.dev/issue/58141).
## Running go tests with wasip1
Building and running a binary is easy, but sometimes we want to be able to run
`go test` directly without having to build and execute the binary manually.
Similar to the `js/wasm` port, the standard library distribution included
in your Go installation comes with a file that makes this very easy. Add the
`misc/wasm` directory to your `PATH` when running Go tests and it will
run the tests using the Wasm host of your choice. This works by `go test`
[automatically executing](https://pkg.go.dev/cmd/go#hdr-Compile_and_run_Go_program)
`misc/wasm/go_wasip1_wasm_exec` when it finds this file in the `PATH`.
```shell
$ export PATH=$PATH:$(go env GOROOT)/misc/wasm
$ GOOS=wasip1 GOARCH=wasm go test ./...
```
This will run `go test` using Wasmtime. The Wasm host used can be controlled
using the environment variable `GOWASIRUNTIME`. Currently supported values
for this variable are `wazero`, `wasmedge`, `wasmtime`, and `wasmer`. This
script is subject to breaking changes between Go versions. Note that Go
`wasip1` binaries dont execute perfectly on all hosts yet (see
[#59907](https://go.dev/issue/59907) and
[#60097](https://go.dev/issue/60097)).
This functionality also works when using `go run`:
```shell
$ GOOS=wasip1 GOARCH=wasm go run ./main.go
Hello world!
```
## Wrapping Wasm functions in Go with go:wasmimport
In addition to the new `wasip1/wasm` port, Go 1.21 introduces a new compiler
directive: `go:wasmimport`. It instructs the compiler to translate calls to
the annotated function into a call to the function specified by the host
module name and function name. This new compiler functionality is what allowed
us to define the `wasip1` syscall API in Go to support the new port, but it
isnt limited to being used in the standard library.
For example, the wasip1 syscall API defines the
[`random_get` function](https://github.com/WebAssembly/WASI/blob/a51a66df5b1db01cf9e873f5537bc5bd552cf770/legacy/preview1/docs.md#-random_getbuf-pointeru8-buf_len-size---result-errno),
and it is exposed to the Go standard library through
[a function wrapper](https://cs.opensource.google/go/go/+/refs/tags/go1.21.0:src/runtime/os_wasip1.go;l=73-75)
defined in the runtime package. It looks like this:
```go
//go:wasmimport wasi_snapshot_preview1 random_get
//go:noescape
func random_get(buf unsafe.Pointer, bufLen size) errno
```
This function wrapper is then wrapped in
[a more ergonomic function](https://cs.opensource.google/go/go/+/refs/tags/go1.21.0:src/runtime/os_wasip1.go;l=183-187)
for use in the standard library:
```go
func getRandomData(r []byte) {
if random_get(unsafe.Pointer(&r[0]), size(len(r))) != 0 {
throw("random_get failed")
}
}
```
This way, a user can call `getRandomData` with a byte slice and it will
eventually make its way to the host-defined `random_get` function. In the same
way, users can define their own wrappers for host functions.
To learn more about the intricacies of wrapping Wasm functions in Go, please
see [the `go:wasmimport` proposal](https://go.dev/issue/59149).
## Limitations
While the `wasip1` port passes all standard library tests, there are some
notable fundamental limitations of the Wasm architecture that may surprise
users.
Wasm is a single threaded architecture with no parallelism. The scheduler can
still schedule goroutines to run concurrently, and standard in/out/error is
non-blocking, so a goroutine can execute while another reads or writes, but any
host function calls (such as requesting random data using the example above)
will cause all goroutines to block until the host function call has returned.
A notable missing feature in the `wasip1` API is a full implementation of
network sockets. `wasip1` only defines functions that operate on already opened
sockets, making it impossible to support some of the most popular features of
the Go standard library, such as HTTP servers. Hosts like Wasmer and WasmEdge
implement extensions to the `wasip1` API, allowing the opening of network
sockets. While these extensions are not implemented by the Go compiler, there
exists a third party library,
[`github.com/stealthrocket/net`](https://github.com/stealthrocket/net), which
uses `go:wasmimport` to allow the use of `net.Dial` and `net.Listen` on
supported Wasm hosts. This enables the creation of `net/http` servers and other
network related functionality when using this package.
## The future of Wasm in Go
The addition of the `wasip1/wasm` port is just the beginning of the Wasm
capabilities we would like to bring to Go. Please keep an eye out on
[the issue tracker](https://github.com/golang/go/issues?q=is%3Aopen+is%3Aissue+label%3Aarch-wasm)
for proposals around exporting Go functions to Wasm (`go:wasmexport`), a 32-bit
port and future WASI API compatibility.
## Get involved
If you are experimenting with and want to contribute to Wasm and Go, please get
involved! The Go issue tracker tracks all in-progress work and the #webassembly
channel on [the Gophers Slack](https://invite.slack.golangbridge.org/) is a
great place to discuss Go and WebAssembly. We look forward to hearing from you!