Repository: tetratelabs/wazero
Pull request #331 (2022-03-04 08:28:18)

Adds tests for multiple goroutines
Adds tests/engine/concurrency_test.go


Patch:
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()


Test patch:
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()
+}