Adds gojs.MustInstantiate to avoid conflicts (#940)

This separate host from guest instantiation in ways similar to other
imports such as emscripten. Doing so allows parallel use of gojs.Run,
provided the ModuleConfig has been assigned a unique name (e.g. via an
atomic number).

Fixes #939

Signed-off-by: Adrian Cole <adrian@tetrate.io>
This commit is contained in:
Crypt Keeper
2022-12-19 18:50:55 +09:00
committed by GitHub
parent b1cb9140dd
commit 1f7f20ee2f
4 changed files with 94 additions and 46 deletions

View File

@@ -34,6 +34,13 @@ func main() {
r := wazero.NewRuntime(ctx)
defer r.Close(ctx) // This closes everything this Runtime created.
// Add the host functions used by `GOARCH=wasm GOOS=js`
start := time.Now()
gojs.MustInstantiate(ctx, r)
goJsInstantiate := time.Since(start).Milliseconds()
log.Printf("gojs.MustInstantiate took %dms", goJsInstantiate)
// Combine the above into our baseline config, overriding defaults.
config := wazero.NewModuleConfig().
// By default, I/O streams are discarded, so you won't see output.
@@ -45,7 +52,7 @@ func main() {
}
// Compile the WebAssembly module using the default configuration.
start := time.Now()
start = time.Now()
compiled, err := r.CompileModule(ctx, bin)
if err != nil {
log.Panicln(err)

View File

@@ -76,6 +76,9 @@ func Benchmark_main(b *testing.B) {
b.Fatal(err)
}
// Add the imports needed for `GOARCH=wasm GOOS=js`
gojs.MustInstantiate(ctx, r)
// Instead of making real HTTP calls, return fake data.
ctx = gojs.WithRoundTripper(ctx, &fakeGitHub{})
cfg := wazero.NewModuleConfig()

View File

@@ -1,5 +1,5 @@
// Package gojs allows you to run wasm binaries compiled by Go when `GOOS=js`
// and `GOARCH=wasm`. See https://wazero.io/languages/go/ for more.
// Package gojs allows you to run wasm binaries compiled by Go when
// `GOARCH=wasm GOOS=js`. See https://wazero.io/languages/go/ for more.
//
// # Experimental
//
@@ -14,10 +14,82 @@ import (
"net/http"
"github.com/tetratelabs/wazero"
"github.com/tetratelabs/wazero/api"
. "github.com/tetratelabs/wazero/internal/gojs"
"github.com/tetratelabs/wazero/internal/wasm"
)
// MustInstantiate calls Instantiate or panics on error.
//
// This is a simpler function for those who know the module "go" is not
// already instantiated, and don't need to unload it.
func MustInstantiate(ctx context.Context, r wazero.Runtime) {
if _, err := Instantiate(ctx, r); err != nil {
panic(err)
}
}
// Instantiate instantiates the "go" module, used by `GOARCH=wasm GOOS=js`,
// into the runtime default namespace.
//
// # Notes
//
// - Failure cases are documented on wazero.Namespace InstantiateModule.
// - Closing the wazero.Runtime has the same effect as closing the result.
// - To add more functions to the "env" module, use FunctionExporter.
// - To instantiate into another wazero.Namespace, use FunctionExporter.
func Instantiate(ctx context.Context, r wazero.Runtime) (api.Closer, error) {
builder := r.NewHostModuleBuilder("go")
NewFunctionExporter().ExportFunctions(builder)
return builder.Instantiate(ctx, r)
}
// FunctionExporter configures the functions in the "go" module used by
// `GOARCH=wasm GOOS=js`.
type FunctionExporter interface {
// ExportFunctions builds functions to export with a
// wazero.HostModuleBuilder named "go".
ExportFunctions(wazero.HostModuleBuilder)
}
// NewFunctionExporter returns a FunctionExporter object.
func NewFunctionExporter() FunctionExporter {
return &functionExporter{}
}
type functionExporter struct{}
// ExportFunctions implements FunctionExporter.ExportFunctions
func (e *functionExporter) ExportFunctions(builder wazero.HostModuleBuilder) {
hfExporter := builder.(wasm.HostFuncExporter)
hfExporter.ExportHostFunc(GetRandomData)
hfExporter.ExportHostFunc(Nanotime1)
hfExporter.ExportHostFunc(WasmExit)
hfExporter.ExportHostFunc(CopyBytesToJS)
hfExporter.ExportHostFunc(ValueCall)
hfExporter.ExportHostFunc(ValueGet)
hfExporter.ExportHostFunc(ValueIndex)
hfExporter.ExportHostFunc(ValueLength)
hfExporter.ExportHostFunc(ValueNew)
hfExporter.ExportHostFunc(ValueSet)
hfExporter.ExportHostFunc(WasmWrite)
hfExporter.ExportHostFunc(ResetMemoryDataView)
hfExporter.ExportHostFunc(Walltime)
hfExporter.ExportHostFunc(ScheduleTimeoutEvent)
hfExporter.ExportHostFunc(ClearTimeoutEvent)
hfExporter.ExportHostFunc(FinalizeRef)
hfExporter.ExportHostFunc(StringVal)
hfExporter.ExportHostFunc(ValueDelete)
hfExporter.ExportHostFunc(ValueSetIndex)
hfExporter.ExportHostFunc(ValueInvoke)
hfExporter.ExportHostFunc(ValuePrepareString)
hfExporter.ExportHostFunc(ValueInstanceOf)
hfExporter.ExportHostFunc(ValueLoadString)
hfExporter.ExportHostFunc(CopyBytesToGo)
hfExporter.ExportHostFunc(Debug)
}
// WithRoundTripper sets the http.RoundTripper used to Run Wasm.
//
// For example, if the code compiled via `GOARCH=wasm GOOS=js` uses
@@ -46,6 +118,10 @@ func WithRoundTripper(ctx context.Context, rt http.RoundTripper) context.Context
//
// // Use compilation cache to reduce performance penalty of multiple runs.
// ctx = experimental.WithCompilationCacheDirName(ctx, ".build")
//
// // Instantiate the host functions used for each call.
// gojs.MustInstantiate(r)
//
// // Execute the "run" function, which corresponds to "main" in stars/main.go.
// err = gojs.Run(ctx, r, compiled, config)
// if exitErr, ok := err.(*sys.ExitError); ok && exitErr.ExitCode() != 0 {
@@ -60,17 +136,10 @@ func WithRoundTripper(ctx context.Context, rt http.RoundTripper) context.Context
// features set by `GOWASM` or used internally by Run.
// - Wasm generated by `GOARCH=wasm GOOS=js` is very slow to compile.
// Use experimental.WithCompilationCacheDirName to improve performance.
// - Both the host and guest module are closed after being run.
func Run(ctx context.Context, r wazero.Runtime, compiled wazero.CompiledModule, config wazero.ModuleConfig) error {
// Instantiate the imports needed by go-compiled wasm.
js, err := hostModuleBuilder(r).Instantiate(ctx, r)
if err != nil {
return err
}
defer js.Close(ctx)
// - The guest module is closed after being run.
func Run(ctx context.Context, ns wazero.Namespace, compiled wazero.CompiledModule, config wazero.ModuleConfig) error {
// Instantiate the module compiled by go, noting it has no init function.
mod, err := r.InstantiateModule(ctx, compiled, config)
mod, err := ns.InstantiateModule(ctx, compiled, config)
if err != nil {
return err
}
@@ -86,36 +155,3 @@ func Run(ctx context.Context, r wazero.Runtime, compiled wazero.CompiledModule,
_, err = mod.ExportedFunction("run").Call(ctx, uint64(argc), uint64(argv))
return err
}
// hostModuleBuilder returns a new wazero.HostModuleBuilder
func hostModuleBuilder(r wazero.Runtime) (builder wazero.HostModuleBuilder) {
builder = r.NewHostModuleBuilder("go")
hfExporter := builder.(wasm.HostFuncExporter)
hfExporter.ExportHostFunc(GetRandomData)
hfExporter.ExportHostFunc(Nanotime1)
hfExporter.ExportHostFunc(WasmExit)
hfExporter.ExportHostFunc(CopyBytesToJS)
hfExporter.ExportHostFunc(ValueCall)
hfExporter.ExportHostFunc(ValueGet)
hfExporter.ExportHostFunc(ValueIndex)
hfExporter.ExportHostFunc(ValueLength)
hfExporter.ExportHostFunc(ValueNew)
hfExporter.ExportHostFunc(ValueSet)
hfExporter.ExportHostFunc(WasmWrite)
hfExporter.ExportHostFunc(ResetMemoryDataView)
hfExporter.ExportHostFunc(Walltime)
hfExporter.ExportHostFunc(ScheduleTimeoutEvent)
hfExporter.ExportHostFunc(ClearTimeoutEvent)
hfExporter.ExportHostFunc(FinalizeRef)
hfExporter.ExportHostFunc(StringVal)
hfExporter.ExportHostFunc(ValueDelete)
hfExporter.ExportHostFunc(ValueSetIndex)
hfExporter.ExportHostFunc(ValueInvoke)
hfExporter.ExportHostFunc(ValuePrepareString)
hfExporter.ExportHostFunc(ValueInstanceOf)
hfExporter.ExportHostFunc(ValueLoadString)
hfExporter.ExportHostFunc(CopyBytesToGo)
hfExporter.ExportHostFunc(Debug)
return
}

View File

@@ -27,6 +27,8 @@ func compileAndRun(ctx context.Context, arg string, config wazero.ModuleConfig)
r := wazero.NewRuntimeWithConfig(testCtx, wazero.NewRuntimeConfig())
defer r.Close(ctx)
gojs.MustInstantiate(ctx, r)
compiled, compileErr := r.CompileModule(ctx, testBin)
if compileErr != nil {
err = compileErr