diff --git a/config_supported.go b/config_supported.go index 69e3553d..13ae5d2b 100644 --- a/config_supported.go +++ b/config_supported.go @@ -2,6 +2,8 @@ package wazero +const JITSupported = true + // NewRuntimeConfig returns NewRuntimeConfigJIT func NewRuntimeConfig() *RuntimeConfig { return NewRuntimeConfigJIT() diff --git a/config_unsupported.go b/config_unsupported.go index 435fb383..c247b7d2 100644 --- a/config_unsupported.go +++ b/config_unsupported.go @@ -2,6 +2,8 @@ package wazero +const JITSupported = false + // NewRuntimeConfig returns NewRuntimeConfigInterpreter func NewRuntimeConfig() *RuntimeConfig { return NewRuntimeConfigInterpreter() diff --git a/tests/engine/adhoc_test.go b/tests/engine/adhoc_test.go index 839668c9..f7c30566 100644 --- a/tests/engine/adhoc_test.go +++ b/tests/engine/adhoc_test.go @@ -5,8 +5,6 @@ import ( _ "embed" "fmt" "math" - "runtime" - "sync" "testing" "github.com/stretchr/testify/require" @@ -19,15 +17,15 @@ import ( // ctx is a default context used to avoid lint warnings even though these tests don't use any context data. var ctx = context.Background() -func TestJIT(t *testing.T) { - if runtime.GOARCH != "amd64" && runtime.GOARCH != "arm64" { +func TestJITAdhoc(t *testing.T) { + if !wazero.JITSupported { t.Skip() } - runTests(t, wazero.NewRuntimeConfigJIT) + runAdhocTests(t, wazero.NewRuntimeConfigJIT) } -func TestInterpreter(t *testing.T) { - runTests(t, wazero.NewRuntimeConfigInterpreter) +func TestInterpreterAdhoc(t *testing.T) { + runAdhocTests(t, wazero.NewRuntimeConfigInterpreter) } var ( @@ -45,7 +43,7 @@ var ( hugestackWasm []byte ) -func runTests(t *testing.T, newRuntimeConfig func() *wazero.RuntimeConfig) { +func runAdhocTests(t *testing.T, newRuntimeConfig func() *wazero.RuntimeConfig) { t.Run("huge stack", func(t *testing.T) { testHugeStack(t, newRuntimeConfig) }) @@ -73,35 +71,18 @@ func runTests(t *testing.T, newRuntimeConfig func() *wazero.RuntimeConfig) { } func testHugeStack(t *testing.T, newRuntimeConfig func() *wazero.RuntimeConfig) { - // We execute 1000 times in order to ensure the JIT engine is stable under high concurrency - // and we have no conflict with Go's runtime. - const goroutines = 1000 - r := wazero.NewRuntimeWithConfig(newRuntimeConfig()) - module, err := r.NewModuleFromSource(hugestackWasm) require.NoError(t, err) fn := module.Function("main") require.NotNil(t, fn) - var wg sync.WaitGroup - wg.Add(goroutines) - for i := 0; i < goroutines; i++ { - go func() { - defer wg.Done() - _, err = fn.Call(ctx) - require.NoError(t, err) - }() - } - wg.Wait() + _, err = fn.Call(ctx) + require.NoError(t, err) } func testFibonacci(t *testing.T, newRuntimeConfig func() *wazero.RuntimeConfig) { - // We execute 1000 times in order to ensure the JIT engine is stable under high concurrency - // and we have no conflict with Go's runtime. - const goroutines = 1000 - r := wazero.NewRuntimeWithConfig(newRuntimeConfig()) module, err := r.NewModuleFromSource(fibWasm) require.NoError(t, err) @@ -109,17 +90,9 @@ func testFibonacci(t *testing.T, newRuntimeConfig func() *wazero.RuntimeConfig) fib := module.Function("fib") require.NotNil(t, fib) - var wg sync.WaitGroup - wg.Add(goroutines) - for i := 0; i < goroutines; i++ { - go func() { - defer wg.Done() - results, err := fib.Call(ctx, 20) - require.NoError(t, err) - require.Equal(t, uint64(10946), results[0]) - }() - } - wg.Wait() + results, err := fib.Call(ctx, 20) + require.NoError(t, err) + require.Equal(t, uint64(10946), results[0]) } func testFac(t *testing.T, newRuntimeConfig func() *wazero.RuntimeConfig) { diff --git a/tests/engine/concurrency_test.go b/tests/engine/concurrency_test.go new file mode 100644 index 00000000..868f166c --- /dev/null +++ b/tests/engine/concurrency_test.go @@ -0,0 +1,68 @@ +package adhoc + +import ( + "sync" + "testing" + + "github.com/tetratelabs/wazero" +) + +func TestJITConcurrency(t *testing.T) { + if !wazero.JITSupported { + t.Skip() + } + runAdhocTestsUnderHighConcurrency(t, wazero.NewRuntimeConfigJIT) + // TODO: Add conccurent instantiation, invocation and release on a single store test case in https://github.com/tetratelabs/wazero/issues/293 + +} + +func TestInterpreterConcurrency(t *testing.T) { + runAdhocTestsUnderHighConcurrency(t, wazero.NewRuntimeConfigInterpreter) + // TODO: Add conccurent instantiation, invocation and release on a single store test case in https://github.com/tetratelabs/wazero/issues/293 +} + +func runAdhocTestsUnderHighConcurrency(t *testing.T, newRuntimeConfig func() *wazero.RuntimeConfig) { + t.Run("huge stack", func(t *testing.T) { + t.Parallel() + runAdhocTestUnderHighConcurrency(t, newRuntimeConfig, testHugeStack) + }) + t.Run("fibonacci", func(t *testing.T) { + t.Parallel() + runAdhocTestUnderHighConcurrency(t, newRuntimeConfig, testFibonacci) + }) + t.Run("fac", func(t *testing.T) { + t.Parallel() + runAdhocTestUnderHighConcurrency(t, newRuntimeConfig, testFac) + }) + t.Run("unreachable", func(t *testing.T) { + t.Parallel() + runAdhocTestUnderHighConcurrency(t, newRuntimeConfig, testUnreachable) + }) + t.Run("memory", func(t *testing.T) { + t.Parallel() + runAdhocTestUnderHighConcurrency(t, newRuntimeConfig, testMemory) + }) + t.Run("recursive entry", func(t *testing.T) { + t.Parallel() + runAdhocTestUnderHighConcurrency(t, newRuntimeConfig, testRecursiveEntry) + }) + t.Run("imported-and-exported func", func(t *testing.T) { + t.Parallel() + runAdhocTestUnderHighConcurrency(t, newRuntimeConfig, testImportedAndExportedFunc) + }) +} + +// runAdhocTestUnderHighConcurrency runs a test case in adhoc_test.go with multiple goroutines. +func runAdhocTestUnderHighConcurrency(t *testing.T, newRuntimeConfig func() *wazero.RuntimeConfig, + adhocTest func(t *testing.T, newRuntimeConfig func() *wazero.RuntimeConfig)) { + const goroutinesPerCase = 1000 + var wg sync.WaitGroup + wg.Add(goroutinesPerCase) + for i := 0; i < goroutinesPerCase; i++ { + go func() { + defer wg.Done() + adhocTest(t, newRuntimeConfig) + }() + } + wg.Wait() +}