wapc

package module
v0.7.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 21, 2025 License: Apache-2.0 Imports: 6 Imported by: 23

README

waPC Host for Go

Gitter PkgGoDev Go Report Card go tests

This is the Golang implementation of the waPC standard for WebAssembly host runtimes. It allows any WebAssembly module to be loaded as a guest and receive requests for invocation as well as to make its own function requests of the host.

Example

The following is a simple example of synchronous, bidirectional procedure calls between a WebAssembly host runtime and the guest module.

package main

import (
	"context"
	"fmt"
	"os"
	"strings"

	"github.com/wapc/wapc-go"
	"github.com/wapc/wapc-go/engines/wazero"
)

func main() {
	if len(os.Args) < 2 {
		fmt.Println("usage: hello <name>")
		return
	}
	name := os.Args[1]
	ctx := context.Background()
	guest, err := os.ReadFile("hello/hello.wasm")
	if err != nil {
		panic(err)
	}

	engine := wazero.Engine()

	module, err := engine.New(ctx, host, guest, &wapc.ModuleConfig{
		Logger: wapc.PrintlnLogger,
		Stdout: os.Stdout,
		Stderr: os.Stderr,
	})
	if err != nil {
		panic(err)
	}
	defer module.Close(ctx)

	instance, err := module.Instantiate(ctx)
	if err != nil {
		panic(err)
	}
	defer instance.Close(ctx)

	result, err := instance.Invoke(ctx, "hello", []byte(name))
	if err != nil {
		panic(err)
	}

	fmt.Println(string(result))
}

func host(ctx context.Context, binding, namespace, operation string, payload []byte) ([]byte, error) {
	// Route the payload to any custom functionality accordingly.
	// You can even route to other waPC modules!!!
	switch namespace {
	case "example":
		switch operation {
		case "capitalize":
			name := string(payload)
			name = strings.Title(name)
			return []byte(name), nil
		}
	}
	return []byte("default"), nil
}

To see this in action, enter the following in your shell:

$ go run example/main.go waPC!
hello called
Hello, WaPC!

Alternatively you can use a Pool to manage a pool of instances.

	pool, err := wapc.NewPool(ctx, module, 10, func(instance wapc.Instance) error {
		// Do something to initialize this instance before use.
		return nil
	})
	if err != nil {
		panic(err)
	}
	defer pool.Close(ctx)

	for i := 0; i < 100; i++ {
		instance, err := pool.Get(10 * time.Millisecond)
		if err != nil {
			panic(err)
		}

		result, err := instance.Invoke(ctx, "hello", []byte("waPC"))
		if err != nil {
			panic(err)
		}

		fmt.Println(string(result))

		err = pool.Return(instance)
		if err != nil {
			panic(err)
		}
	}

While the above example uses wazero, wapc-go is decoupled (via wapc.Engine) and can be used with different runtimes.

Engines

Here are the supported wapc.Engine implementations, in alphabetical order:

Name Usage Build Tag Package
wasmer-go wasmer.Engine() wasmer github.com/wasmerio/wasmer-go
wasmtime-go wasmtime.Engine() wasmtime github.com/bytecodealliance/wasmtime-go
wazero wazero.Engine() N/A github.com/tetratelabs/wazero

For example, to switch the engine to wasmer, change example/main.go like below:

--- a/example/main.go
+++ b/example/main.go
@@ -7,7 +7,7 @@ import (
        "strings"

        "github.com/wapc/wapc-go"
-       "github.com/wapc/wapc-go/engines/wazero"
+       "github.com/wapc/wapc-go/engines/wasmer"
 )

 func main() {
@@ -22,7 +22,7 @@ func main() {
                panic(err)
        }

-       engine := wazero.Engine()
+       engine := wasmer.Engine()

        module, err := engine.New(ctx, code, hostCall)
        if err != nil {

Then, run with its build tag:

$ go run --tags wasmer example/main.go waPC!
hello called
Hello, WaPC!
Differences with wapc-rs (Rust)

Besides engine choices, there differences between this library and the Rust implementation:

  • Separate compilation (New) and instantiation (Instantiate) steps. This is to incur the cost of compilation once in a multi-instance scenario.
  • Pool for creating a pool of instances for a given Module.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func NoOpHostCallHandler

func NoOpHostCallHandler(context.Context, string, string, string, []byte) ([]byte, error)

NoOpHostCallHandler is a noop host call handler to use if your host does not need to support host calls.

func PrintlnLogger added in v0.5.0

func PrintlnLogger(message string)

PrintlnLogger will print the supplied message to standard error. A newline is appended to the end of the message.

Types

type Engine added in v0.4.0

type Engine interface {
	// Name of the engine. Ex. "wazero"
	Name() string

	// New compiles a new WebAssembly module representing the guest, and
	// configures the host functions it uses.
	//   - host: implements host module functions called by the guest
	//	 - guest: the guest WebAssembly binary (%.wasm) to compile
	//   - config: configures the host and guest.
	New(ctx context.Context, host HostCallHandler, guest []byte, config *ModuleConfig) (Module, error)
}

type HostCallHandler

type HostCallHandler func(ctx context.Context, binding, namespace, operation string, payload []byte) ([]byte, error)

HostCallHandler is a function to invoke to handle when a guest is performing a host call.

type Instance

type Instance interface {
	// MemorySize is the size in bytes of the memory available to this Instance.
	MemorySize() uint32

	// Invoke calls `operation` with `payload` on the module and returns a byte slice payload.
	Invoke(ctx context.Context, operation string, payload []byte) ([]byte, error)

	// Close releases resources from this instance, returning the first error encountered.
	// Note: This should be called before calling Module.Close.
	Close(context.Context) error
}

Instance is an instantiated Module

type InstanceInitialize added in v0.5.3

type InstanceInitialize func(instance Instance) error

type Logger

type Logger func(msg string)

Logger is the waPC logger for `__console_log` function calls.

type Module

type Module interface {
	// Instantiate creates a single instance of the module with its own memory.
	Instantiate(context.Context) (Instance, error)

	// Close releases resources from this module, returning the first error encountered.
	// Note: This should be called before after calling Instance.Close on any instances of this module.
	Close(context.Context) error
}

Module is a WebAssembly Module.

type ModuleConfig added in v0.5.0

type ModuleConfig struct {
	// Logger is the logger waPC uses for `__console_log` calls
	Logger Logger
	// Stdout is the writer WASI uses for `fd_write` to file descriptor 1.
	Stdout io.Writer
	// Stderr is the writer WASI uses for `fd_write` to file descriptor 2.
	Stderr io.Writer
}

ModuleConfig includes parameters to Engine.New.

Note: Implementations should copy fields they use instead of storing a reference to this type.

type Pool

type Pool struct {
	// contains filtered or unexported fields
}

Pool is a wrapper around a ringbuffer of WASM modules

func NewPool

func NewPool(ctx context.Context, module Module, size uint64, initializer ...InstanceInitialize) (*Pool, error)

NewPool takes in compiled WASM module and a size and returns a pool containing `size` instances of that module.

func (*Pool) Close

func (p *Pool) Close(ctx context.Context)

Close closes down all the instances contained by the pool.

func (*Pool) Get

func (p *Pool) Get(timeout time.Duration) (Instance, error)

Get returns a module from the pool if it can be retrieved within the passed timeout window, if not it returns an error

func (*Pool) Return

func (p *Pool) Return(inst Instance) error

Return takes a module and adds it to the pool This should only be called using a module

Directories

Path Synopsis
engines
wazero Module
hello module

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL