Allows WithMemoryLimitPages to override the default pages (#1335)

Signed-off-by: Takeshi Yoneda <takeshi@tetrate.io>
This commit is contained in:
Takeshi Yoneda
2023-04-02 17:30:34 -07:00
committed by GitHub
parent f3ef84c9b3
commit fa3090a022
4 changed files with 45 additions and 30 deletions

View File

@@ -48,8 +48,8 @@ type RuntimeConfig interface {
WithCoreFeatures(api.CoreFeatures) RuntimeConfig
// WithMemoryLimitPages overrides the maximum pages allowed per memory. The
// default is 65536, allowing 4GB total memory per instance. Setting a
// value larger than default will panic.
// default is 65536, allowing 4GB total memory per instance if the maximum is
// not encoded in a Wasm binary. Setting a value larger than default will panic.
//
// This example reduces the largest possible memory size from 4GB to 128KB:
// rConfig = wazero.NewRuntimeConfig().WithMemoryLimitPages(2)
@@ -67,6 +67,9 @@ type RuntimeConfig interface {
// rConfig = wazero.NewRuntimeConfig().WithMemoryCapacityFromMax(true)
//
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#grow-mem
//
// Note: if the memory maximum is not encoded in a Wasm binary, this
// results in allocating 4GB. See the doc on WithMemoryLimitPages for detail.
WithMemoryCapacityFromMax(memoryCapacityFromMax bool) RuntimeConfig
// WithDebugInfoEnabled toggles DWARF based stack traces in the face of

View File

@@ -16,15 +16,11 @@ import (
"time"
"github.com/tetratelabs/wazero"
"github.com/tetratelabs/wazero/api"
"github.com/tetratelabs/wazero/experimental/gojs"
"github.com/tetratelabs/wazero/internal/fstest"
internalgojs "github.com/tetratelabs/wazero/internal/gojs"
"github.com/tetratelabs/wazero/internal/gojs/config"
"github.com/tetratelabs/wazero/internal/gojs/run"
"github.com/tetratelabs/wazero/internal/testing/binaryencoding"
"github.com/tetratelabs/wazero/internal/wasm"
binaryformat "github.com/tetratelabs/wazero/internal/wasm/binary"
)
type newConfig func(moduleConfig wazero.ModuleConfig) (wazero.ModuleConfig, *config.Config)
@@ -35,8 +31,11 @@ func defaultConfig(moduleConfig wazero.ModuleConfig) (wazero.ModuleConfig, *conf
func compileAndRun(ctx context.Context, arg string, config newConfig) (stdout, stderr string, err error) {
rt := wazero.NewRuntimeWithConfig(ctx, wazero.NewRuntimeConfig().
// https://github.com/tetratelabs/wazero/issues/992
// In order to avoid race condition on scheduleTimeoutEvent, we need to set the memory max
// and WithMemoryCapacityFromMax(true) above. See #992.
WithMemoryCapacityFromMax(true).
// Set max to a high value, e.g. so that Test_stdio_large can pass.
WithMemoryLimitPages(1024). // 64MB
WithCompilationCache(cache))
return compileAndRunWithRuntime(ctx, rt, arg, config) // use global runtime
}
@@ -113,20 +112,6 @@ func TestMain(m *testing.M) {
log.Panicln(err)
}
// In order to avoid race condition on scheduleTimeoutEvent, we need to set the memory max
// and WithMemoryCapacityFromMax(true) above.
// https://github.com/tetratelabs/wazero/issues/992
//
// TODO: Maybe add WithMemoryMax API?
parsed, err := binaryformat.DecodeModule(testBin, api.CoreFeaturesV2, wasm.MemoryLimitPages, false, false, false)
if err != nil {
log.Panicln(err)
}
// Set max to a high value, e.g. so that Test_stdio_large can pass
parsed.MemorySection.Max = 1024 // 64MB
parsed.MemorySection.IsMaxEncoded = true
testBin = binaryencoding.EncodeModule(parsed)
// Seed wazero's compilation cache to see any error up-front and to prevent
// one test from a cache-miss performance penalty.
r := wazero.NewRuntimeWithConfig(testCtx, wazero.NewRuntimeConfig().WithCompilationCache(cache))

View File

@@ -35,7 +35,7 @@ func DecodeModule(
return nil, ErrInvalidVersion
}
memorySizer := newMemorySizer(memoryLimitPages, memoryCapacityFromMax)
memSizer := newMemorySizer(memoryLimitPages, memoryCapacityFromMax)
m := &wasm.Module{}
var info, line, str, abbrev, ranges []byte // For DWARF Data.
@@ -106,7 +106,7 @@ func DecodeModule(
case wasm.SectionIDType:
m.TypeSection, err = decodeTypeSection(enabledFeatures, r)
case wasm.SectionIDImport:
m.ImportSection, m.ImportFunctionCount, m.ImportGlobalCount, m.ImportMemoryCount, m.ImportTableCount, err = decodeImportSection(r, memorySizer, memoryLimitPages, enabledFeatures)
m.ImportSection, m.ImportFunctionCount, m.ImportGlobalCount, m.ImportMemoryCount, m.ImportTableCount, err = decodeImportSection(r, memSizer, memoryLimitPages, enabledFeatures)
if err != nil {
return nil, err // avoid re-wrapping the error.
}
@@ -115,7 +115,7 @@ func DecodeModule(
case wasm.SectionIDTable:
m.TableSection, err = decodeTableSection(r, enabledFeatures)
case wasm.SectionIDMemory:
m.MemorySection, err = decodeMemorySection(r, memorySizer, memoryLimitPages)
m.MemorySection, err = decodeMemorySection(r, memSizer, memoryLimitPages)
case wasm.SectionIDGlobal:
if m.GlobalSection, err = decodeGlobalSection(r, enabledFeatures); err != nil {
return nil, err // avoid re-wrapping the error.
@@ -185,6 +185,9 @@ func newMemorySizer(memoryLimitPages uint32, memoryCapacityFromMax bool) memoryS
}
return minPages, minPages, *maxPages
}
if memoryCapacityFromMax {
return minPages, memoryLimitPages, memoryLimitPages
}
return minPages, minPages, memoryLimitPages
}
}

View File

@@ -13,32 +13,36 @@ import (
func Test_newMemorySizer(t *testing.T) {
zero := uint32(0)
one := uint32(1)
limit := wasm.MemoryLimitPages
defaultLimit := wasm.MemoryLimitPages
tests := []struct {
name string
memoryCapacityFromMax bool
limit uint32
min uint32
max *uint32
expectedMin, expectedCapacity, expectedMax uint32
}{
{
name: "min 0",
limit: defaultLimit,
min: zero,
max: &limit,
max: &defaultLimit,
expectedMin: zero,
expectedCapacity: zero,
expectedMax: limit,
expectedMax: defaultLimit,
},
{
name: "min 0 defaults max to limit",
name: "min 0 defaults max to defaultLimit",
limit: defaultLimit,
min: zero,
expectedMin: zero,
expectedCapacity: zero,
expectedMax: limit,
expectedMax: defaultLimit,
},
{
name: "min 0, max 0",
limit: defaultLimit,
min: zero,
max: &zero,
expectedMin: zero,
@@ -47,6 +51,7 @@ func Test_newMemorySizer(t *testing.T) {
},
{
name: "min 0, max 1",
limit: defaultLimit,
min: zero,
max: &one,
expectedMin: zero,
@@ -55,6 +60,7 @@ func Test_newMemorySizer(t *testing.T) {
},
{
name: "min 0, max 1 memoryCapacityFromMax",
limit: defaultLimit,
memoryCapacityFromMax: true,
min: zero,
max: &one,
@@ -62,8 +68,26 @@ func Test_newMemorySizer(t *testing.T) {
expectedCapacity: one,
expectedMax: one,
},
{
name: "min 10, no max",
limit: 200,
min: 10,
expectedMin: 10,
expectedCapacity: 10,
expectedMax: 200,
},
{
name: "min 10, no max memoryCapacityFromMax",
memoryCapacityFromMax: true,
limit: 200,
min: 10,
expectedMin: 10,
expectedCapacity: 200,
expectedMax: 200,
},
{
name: "min=max",
limit: defaultLimit,
min: one,
max: &one,
expectedMin: one,
@@ -75,7 +99,7 @@ func Test_newMemorySizer(t *testing.T) {
for _, tt := range tests {
tc := tt
t.Run(tc.name, func(t *testing.T) {
sizer := newMemorySizer(limit, tc.memoryCapacityFromMax)
sizer := newMemorySizer(tc.limit, tc.memoryCapacityFromMax)
min, capacity, max := sizer(tc.min, tc.max)
require.Equal(t, tc.expectedMin, min)
require.Equal(t, tc.expectedCapacity, capacity)