Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Customizable limits #560

Merged
merged 13 commits into from
Oct 14, 2024
6 changes: 1 addition & 5 deletions internal/api/bindings.h
Original file line number Diff line number Diff line change
Expand Up @@ -412,11 +412,7 @@ typedef struct GasReport {
uint64_t used_internally;
} GasReport;

struct cache_t *init_cache(struct ByteSliceView data_dir,
struct ByteSliceView available_capabilities,
uint32_t cache_size,
uint32_t instance_memory_limit,
struct UnmanagedVector *error_msg);
struct cache_t *init_cache(struct ByteSliceView config, struct UnmanagedVector *error_msg);

struct UnmanagedVector save_wasm(struct cache_t *cache,
struct ByteSliceView wasm,
Expand Down
23 changes: 12 additions & 11 deletions internal/api/lib.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ package api
import "C"

import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"runtime"
"strings"
"syscall"
Expand Down Expand Up @@ -41,14 +43,14 @@ type Cache struct {

type Querier = types.Querier

func InitCache(dataDir string, supportedCapabilities []string, cacheSize uint32, instanceMemoryLimit uint32) (Cache, error) {
func InitCache(config types.VMConfig) (Cache, error) {
// libwasmvm would create this directory too but we need it earlier for the lockfile
err := os.MkdirAll(dataDir, 0o755)
err := os.MkdirAll(config.Cache.BaseDir, 0o755)
if err != nil {
return Cache{}, fmt.Errorf("Could not create base directory")
}

lockfile, err := os.OpenFile(dataDir+"/exclusive.lock", os.O_WRONLY|os.O_CREATE, 0o666)
lockfile, err := os.OpenFile(filepath.Join(config.Cache.BaseDir, "exclusive.lock"), os.O_WRONLY|os.O_CREATE, 0o666)
if err != nil {
return Cache{}, fmt.Errorf("Could not open exclusive.lock")
}
Expand All @@ -62,17 +64,16 @@ func InitCache(dataDir string, supportedCapabilities []string, cacheSize uint32,
return Cache{}, fmt.Errorf("Could not lock exclusive.lock. Is a different VM running in the same directory already?")
}

dataDirBytes := []byte(dataDir)
supportedCapabilitiesBytes := []byte(strings.Join(supportedCapabilities, ","))

d := makeView(dataDirBytes)
defer runtime.KeepAlive(dataDirBytes)
capabilitiesView := makeView(supportedCapabilitiesBytes)
defer runtime.KeepAlive(supportedCapabilitiesBytes)
configBytes, err := json.Marshal(config)
if err != nil {
return Cache{}, fmt.Errorf("Could not serialize config")
}
configView := makeView(configBytes)
defer runtime.KeepAlive(configBytes)

errmsg := uninitializedUnmanagedVector()

ptr, err := C.init_cache(d, capabilitiesView, cu32(cacheSize), cu32(instanceMemoryLimit), &errmsg)
ptr, err := C.init_cache(configView, &errmsg)
if err != nil {
return Cache{}, errorWithMessage(err, errmsg)
}
Expand Down
110 changes: 99 additions & 11 deletions internal/api/lib_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,15 @@ func TestInitAndReleaseCache(t *testing.T) {
require.NoError(t, err)
defer os.RemoveAll(tmpdir)

cache, err := InitCache(tmpdir, TESTING_CAPABILITIES, TESTING_CACHE_SIZE, TESTING_MEMORY_LIMIT)
config := types.VMConfig{
Cache: types.CacheOptions{
BaseDir: tmpdir,
AvailableCapabilities: TESTING_CAPABILITIES,
MemoryCacheSizeBytes: types.NewSizeMebi(TESTING_CACHE_SIZE),
InstanceMemoryLimitBytes: types.NewSizeMebi(TESTING_MEMORY_LIMIT),
},
}
cache, err := InitCache(config)
require.NoError(t, err)
ReleaseCache(cache)
}
Expand All @@ -46,7 +54,15 @@ func TestInitCacheWorksForNonExistentDir(t *testing.T) {
defer os.RemoveAll(tmpdir)

createMe := filepath.Join(tmpdir, "does-not-yet-exist")
cache, err := InitCache(createMe, TESTING_CAPABILITIES, TESTING_CACHE_SIZE, TESTING_MEMORY_LIMIT)
config := types.VMConfig{
Cache: types.CacheOptions{
BaseDir: createMe,
AvailableCapabilities: TESTING_CAPABILITIES,
MemoryCacheSizeBytes: types.NewSizeMebi(TESTING_CACHE_SIZE),
InstanceMemoryLimitBytes: types.NewSizeMebi(TESTING_MEMORY_LIMIT),
},
}
cache, err := InitCache(config)
require.NoError(t, err)
ReleaseCache(cache)
}
Expand All @@ -56,7 +72,15 @@ func TestInitCacheErrorsForBrokenDir(t *testing.T) {
// https://gist.github.com/doctaphred/d01d05291546186941e1b7ddc02034d3
// On Unix we should not have permission to create this.
cannotBeCreated := "/foo:bar"
_, err := InitCache(cannotBeCreated, TESTING_CAPABILITIES, TESTING_CACHE_SIZE, TESTING_MEMORY_LIMIT)
config := types.VMConfig{
Cache: types.CacheOptions{
BaseDir: cannotBeCreated,
AvailableCapabilities: TESTING_CAPABILITIES,
MemoryCacheSizeBytes: types.NewSizeMebi(TESTING_CACHE_SIZE),
InstanceMemoryLimitBytes: types.NewSizeMebi(TESTING_MEMORY_LIMIT),
},
}
_, err := InitCache(config)
require.ErrorContains(t, err, "Could not create base directory")
}

Expand All @@ -65,16 +89,40 @@ func TestInitLockingPreventsConcurrentAccess(t *testing.T) {
require.NoError(t, err)
defer os.RemoveAll(tmpdir)

cache1, err1 := InitCache(tmpdir, TESTING_CAPABILITIES, TESTING_CACHE_SIZE, TESTING_MEMORY_LIMIT)
config1 := types.VMConfig{
Cache: types.CacheOptions{
BaseDir: tmpdir,
AvailableCapabilities: TESTING_CAPABILITIES,
MemoryCacheSizeBytes: types.NewSizeMebi(TESTING_CACHE_SIZE),
InstanceMemoryLimitBytes: types.NewSizeMebi(TESTING_MEMORY_LIMIT),
},
}
cache1, err1 := InitCache(config1)
require.NoError(t, err1)

_, err2 := InitCache(tmpdir, TESTING_CAPABILITIES, TESTING_CACHE_SIZE, TESTING_MEMORY_LIMIT)
config2 := types.VMConfig{
Cache: types.CacheOptions{
BaseDir: tmpdir,
AvailableCapabilities: TESTING_CAPABILITIES,
MemoryCacheSizeBytes: types.NewSizeMebi(TESTING_CACHE_SIZE),
InstanceMemoryLimitBytes: types.NewSizeMebi(TESTING_MEMORY_LIMIT),
},
}
_, err2 := InitCache(config2)
require.ErrorContains(t, err2, "Could not lock exclusive.lock")

ReleaseCache(cache1)

// Now we can try again
cache3, err3 := InitCache(tmpdir, TESTING_CAPABILITIES, TESTING_CACHE_SIZE, TESTING_MEMORY_LIMIT)
config3 := types.VMConfig{
Cache: types.CacheOptions{
BaseDir: tmpdir,
AvailableCapabilities: TESTING_CAPABILITIES,
MemoryCacheSizeBytes: types.NewSizeMebi(TESTING_CACHE_SIZE),
InstanceMemoryLimitBytes: types.NewSizeMebi(TESTING_MEMORY_LIMIT),
},
}
cache3, err3 := InitCache(config3)
require.NoError(t, err3)
ReleaseCache(cache3)
}
Expand All @@ -90,11 +138,35 @@ func TestInitLockingAllowsMultipleInstancesInDifferentDirs(t *testing.T) {
defer os.RemoveAll(tmpdir2)
defer os.RemoveAll(tmpdir3)

cache1, err1 := InitCache(tmpdir1, TESTING_CAPABILITIES, TESTING_CACHE_SIZE, TESTING_MEMORY_LIMIT)
config1 := types.VMConfig{
Cache: types.CacheOptions{
BaseDir: tmpdir1,
AvailableCapabilities: TESTING_CAPABILITIES,
MemoryCacheSizeBytes: types.NewSizeMebi(TESTING_CACHE_SIZE),
InstanceMemoryLimitBytes: types.NewSizeMebi(TESTING_MEMORY_LIMIT),
},
}
cache1, err1 := InitCache(config1)
require.NoError(t, err1)
cache2, err2 := InitCache(tmpdir2, TESTING_CAPABILITIES, TESTING_CACHE_SIZE, TESTING_MEMORY_LIMIT)
config2 := types.VMConfig{
Cache: types.CacheOptions{
BaseDir: tmpdir2,
AvailableCapabilities: TESTING_CAPABILITIES,
MemoryCacheSizeBytes: types.NewSizeMebi(TESTING_CACHE_SIZE),
InstanceMemoryLimitBytes: types.NewSizeMebi(TESTING_MEMORY_LIMIT),
},
}
cache2, err2 := InitCache(config2)
require.NoError(t, err2)
cache3, err3 := InitCache(tmpdir3, TESTING_CAPABILITIES, TESTING_CACHE_SIZE, TESTING_MEMORY_LIMIT)
config3 := types.VMConfig{
Cache: types.CacheOptions{
BaseDir: tmpdir3,
AvailableCapabilities: TESTING_CAPABILITIES,
MemoryCacheSizeBytes: types.NewSizeMebi(TESTING_CACHE_SIZE),
InstanceMemoryLimitBytes: types.NewSizeMebi(TESTING_MEMORY_LIMIT),
},
}
cache3, err3 := InitCache(config3)
require.NoError(t, err3)

ReleaseCache(cache1)
Expand All @@ -106,15 +178,31 @@ func TestInitCacheEmptyCapabilities(t *testing.T) {
tmpdir, err := os.MkdirTemp("", "wasmvm-testing")
require.NoError(t, err)
defer os.RemoveAll(tmpdir)
cache, err := InitCache(tmpdir, []string{}, TESTING_CACHE_SIZE, TESTING_MEMORY_LIMIT)
config := types.VMConfig{
Cache: types.CacheOptions{
BaseDir: tmpdir,
AvailableCapabilities: []string{},
MemoryCacheSizeBytes: types.NewSizeMebi(TESTING_CACHE_SIZE),
InstanceMemoryLimitBytes: types.NewSizeMebi(TESTING_MEMORY_LIMIT),
},
}
cache, err := InitCache(config)
require.NoError(t, err)
ReleaseCache(cache)
}

func withCache(t testing.TB) (Cache, func()) {
tmpdir, err := os.MkdirTemp("", "wasmvm-testing")
require.NoError(t, err)
cache, err := InitCache(tmpdir, TESTING_CAPABILITIES, TESTING_CACHE_SIZE, TESTING_MEMORY_LIMIT)
config := types.VMConfig{
Cache: types.CacheOptions{
BaseDir: tmpdir,
AvailableCapabilities: TESTING_CAPABILITIES,
MemoryCacheSizeBytes: types.NewSizeMebi(TESTING_CACHE_SIZE),
InstanceMemoryLimitBytes: types.NewSizeMebi(TESTING_MEMORY_LIMIT),
},
}
cache, err := InitCache(config)
require.NoError(t, err)

cleanup := func() {
Expand Down
16 changes: 15 additions & 1 deletion lib_libwasmvm.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,21 @@ type VM struct {
// `cacheSize` sets the size in MiB of an in-memory cache for e.g. module caching. Set to 0 to disable.
// `deserCost` sets the gas cost of deserializing one byte of data.
func NewVM(dataDir string, supportedCapabilities []string, memoryLimit uint32, printDebug bool, cacheSize uint32) (*VM, error) {
cache, err := api.InitCache(dataDir, supportedCapabilities, cacheSize, memoryLimit)
return NewVMWithConfig(types.VMConfig{
Cache: types.CacheOptions{
BaseDir: dataDir,
AvailableCapabilities: supportedCapabilities,
MemoryCacheSizeBytes: types.NewSizeMebi(cacheSize),
InstanceMemoryLimitBytes: types.NewSizeMebi(memoryLimit),
},
}, printDebug)
}

// NewVMWithConfig creates a new VM with a custom configuration.
// This allows for more fine-grained control over the VM's behavior compared to NewVM and
// can be extended more easily in the future.
func NewVMWithConfig(config types.VMConfig, printDebug bool) (*VM, error) {
cache, err := api.InitCache(config)
if err != nil {
return nil, err
}
Expand Down
6 changes: 1 addition & 5 deletions libwasmvm/bindings.h
Original file line number Diff line number Diff line change
Expand Up @@ -412,11 +412,7 @@ typedef struct GasReport {
uint64_t used_internally;
} GasReport;

struct cache_t *init_cache(struct ByteSliceView data_dir,
struct ByteSliceView available_capabilities,
uint32_t cache_size,
uint32_t instance_memory_limit,
struct UnmanagedVector *error_msg);
struct cache_t *init_cache(struct ByteSliceView config, struct UnmanagedVector *error_msg);

struct UnmanagedVector save_wasm(struct cache_t *cache,
struct ByteSliceView wasm,
Expand Down
3 changes: 1 addition & 2 deletions libwasmvm/src/args.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// store some common string for argument names
pub const DATA_DIR_ARG: &str = "data_dir";
pub const AVAILABLE_CAPABILITIES_ARG: &str = "available_capabilities";
pub const CONFIG_ARG: &str = "config";
pub const CACHE_ARG: &str = "cache";
pub const WASM_ARG: &str = "wasm";
pub const CHECKSUM_ARG: &str = "checksum";
Expand Down
Loading
Loading