Set Memory.Max to min(user-memory-limit, sys-memory-limit) on load (#1165)
Updated `newMemorySizer()` to return the updated value, but also ensure that an invalid `max` still throws an error (invalid module). - Minor cleanup to use the `memorySizer` type instead of the full func signature for clarity - Added a wat+wasm under `internal/integration_test/vs/testdata/` simply because that's where the other cache-related testdata was. Closes #1153. Signed-off-by: Edoardo Vacchi <evacchi@users.noreply.github.com>
This commit is contained in:
@@ -17,6 +17,9 @@ import (
|
||||
//go:embed internal/integration_test/vs/testdata/fac.wasm
|
||||
var facWasm []byte
|
||||
|
||||
//go:embed internal/integration_test/vs/testdata/mem_grow.wasm
|
||||
var memGrowWasm []byte
|
||||
|
||||
func TestCompilationCache(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
// Ensures the normal Wasm module compilation cache works.
|
||||
@@ -81,6 +84,37 @@ func TestCompilationCache(t *testing.T) {
|
||||
// Ensures they are different.
|
||||
require.NotEqual(t, fooCompiled, barCompiled)
|
||||
})
|
||||
|
||||
t.Run("memory limit should not affect caches", func(t *testing.T) {
|
||||
// Creates new cache instance and pass it to the config.
|
||||
c := NewCompilationCache()
|
||||
config := NewRuntimeConfig().WithCompilationCache(c)
|
||||
|
||||
// create two different runtimes with separate memory limits
|
||||
rt0 := NewRuntimeWithConfig(ctx, config)
|
||||
rt1 := NewRuntimeWithConfig(ctx, config.WithMemoryLimitPages(2))
|
||||
rt2 := NewRuntimeWithConfig(ctx, config.WithMemoryLimitPages(4))
|
||||
|
||||
// the compiled module is not equal because the memory limits are applied to the Memory instance
|
||||
module0, _ := rt0.CompileModule(ctx, memGrowWasm)
|
||||
module1, _ := rt1.CompileModule(ctx, memGrowWasm)
|
||||
module2, _ := rt2.CompileModule(ctx, memGrowWasm)
|
||||
|
||||
max0, _ := module0.ExportedMemories()["memory"].Max()
|
||||
max1, _ := module1.ExportedMemories()["memory"].Max()
|
||||
max2, _ := module2.ExportedMemories()["memory"].Max()
|
||||
require.Equal(t, uint32(5), max0)
|
||||
require.Equal(t, uint32(2), max1)
|
||||
require.Equal(t, uint32(4), max2)
|
||||
|
||||
compiledModule0 := module0.(*compiledModule)
|
||||
compiledModule1 := module1.(*compiledModule)
|
||||
compiledModule2 := module2.(*compiledModule)
|
||||
|
||||
// compare the compiled engine which contains the underlying "codes"
|
||||
require.Equal(t, compiledModule0.compiledEngine, compiledModule1.compiledEngine)
|
||||
require.Equal(t, compiledModule1.compiledEngine, compiledModule2.compiledEngine)
|
||||
})
|
||||
}
|
||||
|
||||
func getCacheSharedRuntimes(ctx context.Context, t *testing.T) (foo, bar *runtime) {
|
||||
|
||||
BIN
internal/integration_test/vs/testdata/mem_grow.wasm
vendored
Normal file
BIN
internal/integration_test/vs/testdata/mem_grow.wasm
vendored
Normal file
Binary file not shown.
14
internal/integration_test/vs/testdata/mem_grow.wat
vendored
Normal file
14
internal/integration_test/vs/testdata/mem_grow.wat
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
(module
|
||||
(memory (export "memory") 1 5) ;; start with one memory page, and max of 4 pages
|
||||
(func $main
|
||||
|
||||
(loop $my_loop
|
||||
(i32.gt_s
|
||||
(memory.grow (i32.const 1))
|
||||
(i32.const 0))
|
||||
br_if $my_loop
|
||||
unreachable)
|
||||
return
|
||||
)
|
||||
(start $main)
|
||||
)
|
||||
@@ -174,6 +174,14 @@ func newMemorySizer(memoryLimitPages uint32, memoryCapacityFromMax bool) memoryS
|
||||
if memoryCapacityFromMax {
|
||||
return minPages, *maxPages, *maxPages
|
||||
}
|
||||
// This is an invalid value: let it propagate, we will fail later.
|
||||
if *maxPages > wasm.MemoryLimitPages {
|
||||
return minPages, minPages, *maxPages
|
||||
}
|
||||
// This is a valid value, but it goes over the run-time limit: return the limit.
|
||||
if *maxPages > memoryLimitPages {
|
||||
return minPages, memoryLimitPages, memoryLimitPages
|
||||
}
|
||||
return minPages, minPages, *maxPages
|
||||
}
|
||||
return minPages, minPages, memoryLimitPages
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
func decodeImport(
|
||||
r *bytes.Reader,
|
||||
idx uint32,
|
||||
memorySizer func(minPages uint32, maxPages *uint32) (min, capacity, max uint32),
|
||||
memorySizer memorySizer,
|
||||
memoryLimitPages uint32,
|
||||
enabledFeatures api.CoreFeatures,
|
||||
) (i *wasm.Import, err error) {
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-memory
|
||||
func decodeMemory(
|
||||
r *bytes.Reader,
|
||||
memorySizer func(minPages uint32, maxPages *uint32) (min, capacity, max uint32),
|
||||
memorySizer memorySizer,
|
||||
memoryLimitPages uint32,
|
||||
) (*wasm.Memory, error) {
|
||||
min, maxP, err := decodeLimitsType(r)
|
||||
|
||||
@@ -88,9 +88,10 @@ func TestMemoryType(t *testing.T) {
|
||||
max := wasm.MemoryLimitPages
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
input *wasm.Memory
|
||||
expected []byte
|
||||
name string
|
||||
input *wasm.Memory
|
||||
memoryLimitPages uint32
|
||||
expected []byte
|
||||
}{
|
||||
{
|
||||
name: "min 0",
|
||||
@@ -122,6 +123,12 @@ func TestMemoryType(t *testing.T) {
|
||||
input: &wasm.Memory{Min: max, Cap: max, Max: max, IsMaxEncoded: true},
|
||||
expected: []byte{0x1, 0x80, 0x80, 0x4, 0x80, 0x80, 0x4},
|
||||
},
|
||||
{
|
||||
name: "min 0, max largest, wazero limit",
|
||||
input: &wasm.Memory{Max: max, IsMaxEncoded: true},
|
||||
memoryLimitPages: 512,
|
||||
expected: []byte{0x1, 0, 0x80, 0x80, 0x4},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
@@ -133,9 +140,18 @@ func TestMemoryType(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("decode %s", tc.name), func(t *testing.T) {
|
||||
binary, err := decodeMemory(bytes.NewReader(b), newMemorySizer(max, false), max)
|
||||
tmax := max
|
||||
expectedDecoded := tc.input
|
||||
if tc.memoryLimitPages != 0 {
|
||||
// If a memory limit exists, then the expected module Max, Cap reflect that limit.
|
||||
tmax = tc.memoryLimitPages
|
||||
expectedDecoded.Max = tmax
|
||||
expectedDecoded.Cap = tmax
|
||||
}
|
||||
|
||||
binary, err := decodeMemory(bytes.NewReader(b), newMemorySizer(tmax, false), tmax)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, binary, tc.input)
|
||||
require.Equal(t, binary, expectedDecoded)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ func decodeTypeSection(enabledFeatures api.CoreFeatures, r *bytes.Reader) ([]*wa
|
||||
|
||||
func decodeImportSection(
|
||||
r *bytes.Reader,
|
||||
memorySizer func(minPages uint32, maxPages *uint32) (min, capacity, max uint32),
|
||||
memorySizer memorySizer,
|
||||
memoryLimitPages uint32,
|
||||
enabledFeatures api.CoreFeatures,
|
||||
) ([]*wasm.Import, error) {
|
||||
@@ -84,7 +84,7 @@ func decodeTableSection(r *bytes.Reader, enabledFeatures api.CoreFeatures) ([]*w
|
||||
|
||||
func decodeMemorySection(
|
||||
r *bytes.Reader,
|
||||
memorySizer func(minPages uint32, maxPages *uint32) (min, capacity, max uint32),
|
||||
memorySizer memorySizer,
|
||||
memoryLimitPages uint32,
|
||||
) (*wasm.Memory, error) {
|
||||
vs, _, err := leb128.DecodeUint32(r)
|
||||
|
||||
Reference in New Issue
Block a user