Allows WithMemoryLimitPages to override the default pages (#1335)
Signed-off-by: Takeshi Yoneda <takeshi@tetrate.io>
This commit is contained in:
@@ -48,8 +48,8 @@ type RuntimeConfig interface {
|
|||||||
WithCoreFeatures(api.CoreFeatures) RuntimeConfig
|
WithCoreFeatures(api.CoreFeatures) RuntimeConfig
|
||||||
|
|
||||||
// WithMemoryLimitPages overrides the maximum pages allowed per memory. The
|
// WithMemoryLimitPages overrides the maximum pages allowed per memory. The
|
||||||
// default is 65536, allowing 4GB total memory per instance. Setting a
|
// default is 65536, allowing 4GB total memory per instance if the maximum is
|
||||||
// value larger than default will panic.
|
// 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:
|
// This example reduces the largest possible memory size from 4GB to 128KB:
|
||||||
// rConfig = wazero.NewRuntimeConfig().WithMemoryLimitPages(2)
|
// rConfig = wazero.NewRuntimeConfig().WithMemoryLimitPages(2)
|
||||||
@@ -67,6 +67,9 @@ type RuntimeConfig interface {
|
|||||||
// rConfig = wazero.NewRuntimeConfig().WithMemoryCapacityFromMax(true)
|
// rConfig = wazero.NewRuntimeConfig().WithMemoryCapacityFromMax(true)
|
||||||
//
|
//
|
||||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#grow-mem
|
// 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
|
WithMemoryCapacityFromMax(memoryCapacityFromMax bool) RuntimeConfig
|
||||||
|
|
||||||
// WithDebugInfoEnabled toggles DWARF based stack traces in the face of
|
// WithDebugInfoEnabled toggles DWARF based stack traces in the face of
|
||||||
|
|||||||
@@ -16,15 +16,11 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/tetratelabs/wazero"
|
"github.com/tetratelabs/wazero"
|
||||||
"github.com/tetratelabs/wazero/api"
|
|
||||||
"github.com/tetratelabs/wazero/experimental/gojs"
|
"github.com/tetratelabs/wazero/experimental/gojs"
|
||||||
"github.com/tetratelabs/wazero/internal/fstest"
|
"github.com/tetratelabs/wazero/internal/fstest"
|
||||||
internalgojs "github.com/tetratelabs/wazero/internal/gojs"
|
internalgojs "github.com/tetratelabs/wazero/internal/gojs"
|
||||||
"github.com/tetratelabs/wazero/internal/gojs/config"
|
"github.com/tetratelabs/wazero/internal/gojs/config"
|
||||||
"github.com/tetratelabs/wazero/internal/gojs/run"
|
"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)
|
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) {
|
func compileAndRun(ctx context.Context, arg string, config newConfig) (stdout, stderr string, err error) {
|
||||||
rt := wazero.NewRuntimeWithConfig(ctx, wazero.NewRuntimeConfig().
|
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).
|
WithMemoryCapacityFromMax(true).
|
||||||
|
// Set max to a high value, e.g. so that Test_stdio_large can pass.
|
||||||
|
WithMemoryLimitPages(1024). // 64MB
|
||||||
WithCompilationCache(cache))
|
WithCompilationCache(cache))
|
||||||
return compileAndRunWithRuntime(ctx, rt, arg, config) // use global runtime
|
return compileAndRunWithRuntime(ctx, rt, arg, config) // use global runtime
|
||||||
}
|
}
|
||||||
@@ -113,20 +112,6 @@ func TestMain(m *testing.M) {
|
|||||||
log.Panicln(err)
|
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
|
// Seed wazero's compilation cache to see any error up-front and to prevent
|
||||||
// one test from a cache-miss performance penalty.
|
// one test from a cache-miss performance penalty.
|
||||||
r := wazero.NewRuntimeWithConfig(testCtx, wazero.NewRuntimeConfig().WithCompilationCache(cache))
|
r := wazero.NewRuntimeWithConfig(testCtx, wazero.NewRuntimeConfig().WithCompilationCache(cache))
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ func DecodeModule(
|
|||||||
return nil, ErrInvalidVersion
|
return nil, ErrInvalidVersion
|
||||||
}
|
}
|
||||||
|
|
||||||
memorySizer := newMemorySizer(memoryLimitPages, memoryCapacityFromMax)
|
memSizer := newMemorySizer(memoryLimitPages, memoryCapacityFromMax)
|
||||||
|
|
||||||
m := &wasm.Module{}
|
m := &wasm.Module{}
|
||||||
var info, line, str, abbrev, ranges []byte // For DWARF Data.
|
var info, line, str, abbrev, ranges []byte // For DWARF Data.
|
||||||
@@ -106,7 +106,7 @@ func DecodeModule(
|
|||||||
case wasm.SectionIDType:
|
case wasm.SectionIDType:
|
||||||
m.TypeSection, err = decodeTypeSection(enabledFeatures, r)
|
m.TypeSection, err = decodeTypeSection(enabledFeatures, r)
|
||||||
case wasm.SectionIDImport:
|
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 {
|
if err != nil {
|
||||||
return nil, err // avoid re-wrapping the error.
|
return nil, err // avoid re-wrapping the error.
|
||||||
}
|
}
|
||||||
@@ -115,7 +115,7 @@ func DecodeModule(
|
|||||||
case wasm.SectionIDTable:
|
case wasm.SectionIDTable:
|
||||||
m.TableSection, err = decodeTableSection(r, enabledFeatures)
|
m.TableSection, err = decodeTableSection(r, enabledFeatures)
|
||||||
case wasm.SectionIDMemory:
|
case wasm.SectionIDMemory:
|
||||||
m.MemorySection, err = decodeMemorySection(r, memorySizer, memoryLimitPages)
|
m.MemorySection, err = decodeMemorySection(r, memSizer, memoryLimitPages)
|
||||||
case wasm.SectionIDGlobal:
|
case wasm.SectionIDGlobal:
|
||||||
if m.GlobalSection, err = decodeGlobalSection(r, enabledFeatures); err != nil {
|
if m.GlobalSection, err = decodeGlobalSection(r, enabledFeatures); err != nil {
|
||||||
return nil, err // avoid re-wrapping the error.
|
return nil, err // avoid re-wrapping the error.
|
||||||
@@ -185,6 +185,9 @@ func newMemorySizer(memoryLimitPages uint32, memoryCapacityFromMax bool) memoryS
|
|||||||
}
|
}
|
||||||
return minPages, minPages, *maxPages
|
return minPages, minPages, *maxPages
|
||||||
}
|
}
|
||||||
|
if memoryCapacityFromMax {
|
||||||
|
return minPages, memoryLimitPages, memoryLimitPages
|
||||||
|
}
|
||||||
return minPages, minPages, memoryLimitPages
|
return minPages, minPages, memoryLimitPages
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,32 +13,36 @@ import (
|
|||||||
func Test_newMemorySizer(t *testing.T) {
|
func Test_newMemorySizer(t *testing.T) {
|
||||||
zero := uint32(0)
|
zero := uint32(0)
|
||||||
one := uint32(1)
|
one := uint32(1)
|
||||||
limit := wasm.MemoryLimitPages
|
defaultLimit := wasm.MemoryLimitPages
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
memoryCapacityFromMax bool
|
memoryCapacityFromMax bool
|
||||||
|
limit uint32
|
||||||
min uint32
|
min uint32
|
||||||
max *uint32
|
max *uint32
|
||||||
expectedMin, expectedCapacity, expectedMax uint32
|
expectedMin, expectedCapacity, expectedMax uint32
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "min 0",
|
name: "min 0",
|
||||||
|
limit: defaultLimit,
|
||||||
min: zero,
|
min: zero,
|
||||||
max: &limit,
|
max: &defaultLimit,
|
||||||
expectedMin: zero,
|
expectedMin: zero,
|
||||||
expectedCapacity: 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,
|
min: zero,
|
||||||
expectedMin: zero,
|
expectedMin: zero,
|
||||||
expectedCapacity: zero,
|
expectedCapacity: zero,
|
||||||
expectedMax: limit,
|
expectedMax: defaultLimit,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "min 0, max 0",
|
name: "min 0, max 0",
|
||||||
|
limit: defaultLimit,
|
||||||
min: zero,
|
min: zero,
|
||||||
max: &zero,
|
max: &zero,
|
||||||
expectedMin: zero,
|
expectedMin: zero,
|
||||||
@@ -47,6 +51,7 @@ func Test_newMemorySizer(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "min 0, max 1",
|
name: "min 0, max 1",
|
||||||
|
limit: defaultLimit,
|
||||||
min: zero,
|
min: zero,
|
||||||
max: &one,
|
max: &one,
|
||||||
expectedMin: zero,
|
expectedMin: zero,
|
||||||
@@ -55,6 +60,7 @@ func Test_newMemorySizer(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "min 0, max 1 memoryCapacityFromMax",
|
name: "min 0, max 1 memoryCapacityFromMax",
|
||||||
|
limit: defaultLimit,
|
||||||
memoryCapacityFromMax: true,
|
memoryCapacityFromMax: true,
|
||||||
min: zero,
|
min: zero,
|
||||||
max: &one,
|
max: &one,
|
||||||
@@ -62,8 +68,26 @@ func Test_newMemorySizer(t *testing.T) {
|
|||||||
expectedCapacity: one,
|
expectedCapacity: one,
|
||||||
expectedMax: 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",
|
name: "min=max",
|
||||||
|
limit: defaultLimit,
|
||||||
min: one,
|
min: one,
|
||||||
max: &one,
|
max: &one,
|
||||||
expectedMin: one,
|
expectedMin: one,
|
||||||
@@ -75,7 +99,7 @@ func Test_newMemorySizer(t *testing.T) {
|
|||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
tc := tt
|
tc := tt
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
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)
|
min, capacity, max := sizer(tc.min, tc.max)
|
||||||
require.Equal(t, tc.expectedMin, min)
|
require.Equal(t, tc.expectedMin, min)
|
||||||
require.Equal(t, tc.expectedCapacity, capacity)
|
require.Equal(t, tc.expectedCapacity, capacity)
|
||||||
|
|||||||
Reference in New Issue
Block a user