WASI Reactor i Zig och Go
Ett litet exempel på hur en WASI Reactor skriven i Zig kan integreras och exekveras i Go med hjälp av Wazero.
Bakgrund
I början av februari släpptes Go 1.24.
Bland mycket annat fick vi flera förbättringar när det gäller stödet för WebAssembly
(och WASI).
Man publicerade även bloggposten Extensible Wasm Applications with Go,
där en liten reaktorbinär kompileras via GOOS=wasip1 GOARCH=wasm go build -buildmode=c-shared -o reactor.wasm
Detta fick mig att fundera på hur smidigt det skulle vara att istället använda Zig för en sådan binär och sedan använda den från Go (via Wazero). Väldigt smidigt visade det sig.
Reaktor skriven i Zig ⚡
reactor.zig
Vi börjar med att implementera en minimal binär i Zig, bestående av två exporterade funktioner.
const std = @import("std");
var s: i32 = undefined;
export fn _initialize() void {
s = 20;
std.debug.print("s = {d}\n", .{s});
}
export fn inc(n: i32) i32 {
s += n;
return s;
}
Notera
std.debug.print
kräver att.wasm
-binären får tillgång till Stderr av sin WebAssembly-körmiljö.
build.zig
Vi använder oss sedan av det inbyggda byggsystemet i Zig för att deklarera hur vi vill att vår binär ska byggas;
- Mål
.{ .cpu_arch = .wasm32, .os_tag = .wasi }
- Optimeringsnivå
.ReleaseSmall
const std = @import("std");
pub fn build(b: *std.Build) void {
const exe = b.addExecutable(.{
.name = "reactor",
.root_source_file = b.path("reactor.zig"),
.target = b.resolveTargetQuery(.{
.cpu_arch = .wasm32,
.os_tag = .wasi,
}),
.optimize = .ReleaseSmall,
});
exe.rdynamic = true;
exe.wasi_exec_model = .reactor;
b.installArtifact(exe);
}
Notera
exe.wasi_exec_model = .reactor
används för att byta exekveringsmodell.
zig build
bör resultera i en .wasm
-binär (zig-out/bin/reactor.wasm
), redo att laddas i en WebAssembly-körmiljö.
Tips
Om du har installerat The WebAssembly Binary Toolkit så kan du inspektera binären som text med hjälp av
wasm2wat -f zig-out/bin/reactor.wasm
Exekvering av .wasm
-binären i Go (via Wazero)
Nu är det dags att skriva ett litet program i Go som agerar WebAssembly-körmiljö för den .wasm
-binär vi skrev i Zig.
Praktiskt nog så kan vi importera modulen Wazero för att göra precis detta.
Wazero hämtas enklast till din Go-modul genom:
$ go get github.com/tetratelabs/wazero@latest
main.go
package main
import (
"context"
"fmt"
"io"
"os"
"os/signal"
"syscall"
_ "embed"
"github.com/tetratelabs/wazero"
"github.com/tetratelabs/wazero/api"
"github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1"
)
//go:generate zig build
//go:embed zig-out/bin/reactor.wasm
var source []byte
func main() {
ctx, stop := signal.NotifyContext(context.Background(),
os.Interrupt,
syscall.SIGTERM,
)
defer stop()
if err := run(ctx, os.Stdout); err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
}
func run(ctx context.Context, w io.Writer) error {
r := wazero.NewRuntime(ctx)
defer r.Close(ctx)
wasi_snapshot_preview1.MustInstantiate(ctx, r)
// Calls _initialize, if exported
mod, err := r.InstantiateWithConfig(ctx, source, wazero.
NewModuleConfig().
WithStartFunctions("_initialize").
WithStderr(os.Stderr),
)
if err != nil {
return err
}
inc := func(ctx context.Context) func(n int32) (int32, error) {
// Handle to the exported function `inc`
fn := mod.ExportedFunction("inc")
return func(n int32) (int32, error) {
res, err := fn.Call(ctx, api.EncodeI32(n))
if err != nil {
return -1, err
}
return api.DecodeI32(res[0]), nil
}
}(ctx)
var a int32 = 1
// Call the function.
b, err := inc(a)
if err != nil {
return err
}
fmt.Fprintf(w, "inc(%d) = %d\n", a, b)
// The instance is still alive.
//
// We can call the function again.
c, err := inc(b)
if err != nil {
return err
}
fmt.Fprintf(w, "inc(%d) = %d\n", b, c)
return nil
}
Notera
//go:generate zig build
låter oss användago generate
för att bygga reaktorn medzig build
//go:embed zig-out/bin/reactor.wasm
bäddar in.wasm
-filen i den binär som Go-kompilatorn producerar.
Slutresultat
Vi bör nu få detta resultat när vi kör go generate && go run .
s = 20
inc(1) = 21
inc(21) = 42
/ Peter
När denna artikel skrevs var de aktuella versionerna för Go 1.24.1
och Zig 0.14.0