Remove the ModuleLimits pooling configuration structure (#3837)
* Remove the `ModuleLimits` pooling configuration structure
This commit is an attempt to improve the usability of the pooling
allocator by removing the need to configure a `ModuleLimits` structure.
Internally this structure has limits on all forms of wasm constructs but
this largely bottoms out in the size of an allocation for an instance in
the instance pooling allocator. Maintaining this list of limits can be
cumbersome as modules may get tweaked over time and there's otherwise no
real reason to limit the number of globals in a module since the main
goal is to limit the memory consumption of a `VMContext` which can be
done with a memory allocation limit rather than fine-tuned control over
each maximum and minimum.
The new approach taken in this commit is to remove `ModuleLimits`. Some
fields, such as `tables`, `table_elements` , `memories`, and
`memory_pages` are moved to `InstanceLimits` since they're still
enforced at runtime. A new field `size` is added to `InstanceLimits`
which indicates, in bytes, the maximum size of the `VMContext`
allocation. If the size of a `VMContext` for a module exceeds this value
then instantiation will fail.
This involved adding a few more checks to `{Table, Memory}::new_static`
to ensure that the minimum size is able to fit in the allocation, since
previously modules were validated at compile time of the module that
everything fit and that validation no longer happens (it happens at
runtime).
A consequence of this commit is that Wasmtime will have no built-in way
to reject modules at compile time if they'll fail to be instantiated
within a particular pooling allocator configuration. Instead a module
must attempt instantiation see if a failure happens.
* Fix benchmark compiles
* Fix some doc links
* Fix a panic by ensuring modules have limited tables/memories
* Review comments
* Add back validation at `Module` time instantiation is possible
This allows for getting an early signal at compile time that a module
will never be instantiable in an engine with matching settings.
* Provide a better error message when sizes are exceeded
Improve the error message when an instance size exceeds the maximum by
providing a breakdown of where the bytes are all going and why the large
size is being requested.
* Try to fix test in qemu
* Flag new test as 64-bit only
Sizes are all specific to 64-bit right now
This commit is contained in:
@@ -209,13 +209,10 @@ fn strategies() -> impl Iterator<Item = InstanceAllocationStrategy> {
|
|||||||
InstanceAllocationStrategy::OnDemand,
|
InstanceAllocationStrategy::OnDemand,
|
||||||
InstanceAllocationStrategy::Pooling {
|
InstanceAllocationStrategy::Pooling {
|
||||||
strategy: Default::default(),
|
strategy: Default::default(),
|
||||||
module_limits: ModuleLimits {
|
instance_limits: InstanceLimits {
|
||||||
functions: 40_000,
|
memory_pages: 10_000,
|
||||||
memory_pages: 1_000,
|
..Default::default()
|
||||||
types: 200,
|
|
||||||
..ModuleLimits::default()
|
|
||||||
},
|
},
|
||||||
instance_limits: InstanceLimits::default(),
|
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -94,11 +94,11 @@ fn test_setup() -> (Engine, Module) {
|
|||||||
let mut config = Config::new();
|
let mut config = Config::new();
|
||||||
config.allocation_strategy(InstanceAllocationStrategy::Pooling {
|
config.allocation_strategy(InstanceAllocationStrategy::Pooling {
|
||||||
strategy: PoolingAllocationStrategy::NextAvailable,
|
strategy: PoolingAllocationStrategy::NextAvailable,
|
||||||
module_limits: ModuleLimits {
|
instance_limits: InstanceLimits {
|
||||||
|
count: pool_count,
|
||||||
memory_pages: 1,
|
memory_pages: 1,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
instance_limits: InstanceLimits { count: pool_count },
|
|
||||||
});
|
});
|
||||||
let engine = Engine::new(&config).unwrap();
|
let engine = Engine::new(&config).unwrap();
|
||||||
|
|
||||||
|
|||||||
@@ -154,6 +154,67 @@ impl<P: PtrSize> VMOffsets<P> {
|
|||||||
pub fn pointer_size(&self) -> u8 {
|
pub fn pointer_size(&self) -> u8 {
|
||||||
self.ptr.size()
|
self.ptr.size()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns an iterator which provides a human readable description and a
|
||||||
|
/// byte size. The iterator returned will iterate over the bytes allocated
|
||||||
|
/// to the entire `VMOffsets` structure to explain where each byte size is
|
||||||
|
/// coming from.
|
||||||
|
pub fn region_sizes(&self) -> impl Iterator<Item = (&str, u32)> {
|
||||||
|
macro_rules! calculate_sizes {
|
||||||
|
($($name:ident: $desc:tt,)*) => {{
|
||||||
|
let VMOffsets {
|
||||||
|
// These fields are metadata not talking about specific
|
||||||
|
// offsets of specific fields.
|
||||||
|
ptr: _,
|
||||||
|
num_imported_functions: _,
|
||||||
|
num_imported_tables: _,
|
||||||
|
num_imported_memories: _,
|
||||||
|
num_imported_globals: _,
|
||||||
|
num_defined_tables: _,
|
||||||
|
num_defined_globals: _,
|
||||||
|
num_defined_memories: _,
|
||||||
|
num_defined_functions: _,
|
||||||
|
|
||||||
|
// used as the initial size below
|
||||||
|
size,
|
||||||
|
|
||||||
|
// exhaustively match teh rest of the fields with input from
|
||||||
|
// the macro
|
||||||
|
$($name,)*
|
||||||
|
} = *self;
|
||||||
|
|
||||||
|
// calculate the size of each field by relying on the inputs to
|
||||||
|
// the macro being in reverse order and determining the size of
|
||||||
|
// the field as the offset from the field to the last field.
|
||||||
|
let mut last = size;
|
||||||
|
$(
|
||||||
|
assert!($name <= last);
|
||||||
|
let tmp = $name;
|
||||||
|
let $name = last - $name;
|
||||||
|
last = tmp;
|
||||||
|
)*
|
||||||
|
assert_eq!(last, 0);
|
||||||
|
IntoIterator::into_iter([$(($desc, $name),)*])
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
calculate_sizes! {
|
||||||
|
defined_anyfuncs: "module functions",
|
||||||
|
defined_globals: "defined globals",
|
||||||
|
defined_memories: "defined memories",
|
||||||
|
defined_tables: "defined tables",
|
||||||
|
imported_globals: "imported globals",
|
||||||
|
imported_memories: "imported memories",
|
||||||
|
imported_tables: "imported tables",
|
||||||
|
imported_functions: "imported functions",
|
||||||
|
signature_ids: "module types",
|
||||||
|
builtin_functions: "jit builtin functions state",
|
||||||
|
store: "jit store state",
|
||||||
|
externref_activations_table: "jit host externref state",
|
||||||
|
epoch_ptr: "jit current epoch state",
|
||||||
|
interrupts: "jit interrupt state",
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P: PtrSize> From<VMOffsetsFields<P>> for VMOffsets<P> {
|
impl<P: PtrSize> From<VMOffsetsFields<P>> for VMOffsets<P> {
|
||||||
|
|||||||
@@ -60,79 +60,28 @@ impl PoolingAllocationStrategy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Configuration for `wasmtime::ModuleLimits`.
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
|
|
||||||
pub struct ModuleLimits {
|
|
||||||
imported_functions: u32,
|
|
||||||
imported_tables: u32,
|
|
||||||
imported_memories: u32,
|
|
||||||
imported_globals: u32,
|
|
||||||
types: u32,
|
|
||||||
functions: u32,
|
|
||||||
tables: u32,
|
|
||||||
memories: u32,
|
|
||||||
/// The maximum number of globals that can be defined in a module.
|
|
||||||
pub globals: u32,
|
|
||||||
table_elements: u32,
|
|
||||||
memory_pages: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ModuleLimits {
|
|
||||||
fn to_wasmtime(&self) -> wasmtime::ModuleLimits {
|
|
||||||
wasmtime::ModuleLimits {
|
|
||||||
imported_functions: self.imported_functions,
|
|
||||||
imported_tables: self.imported_tables,
|
|
||||||
imported_memories: self.imported_memories,
|
|
||||||
imported_globals: self.imported_globals,
|
|
||||||
types: self.types,
|
|
||||||
functions: self.functions,
|
|
||||||
tables: self.tables,
|
|
||||||
memories: self.memories,
|
|
||||||
globals: self.globals,
|
|
||||||
table_elements: self.table_elements,
|
|
||||||
memory_pages: self.memory_pages,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Arbitrary<'a> for ModuleLimits {
|
|
||||||
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
|
|
||||||
const MAX_IMPORTS: u32 = 1000;
|
|
||||||
const MAX_TYPES: u32 = 1000;
|
|
||||||
const MAX_FUNCTIONS: u32 = 1000;
|
|
||||||
const MAX_TABLES: u32 = 10;
|
|
||||||
const MAX_MEMORIES: u32 = 10;
|
|
||||||
const MAX_GLOBALS: u32 = 1000;
|
|
||||||
const MAX_ELEMENTS: u32 = 1000;
|
|
||||||
const MAX_MEMORY_PAGES: u64 = 160; // 10 MiB
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
imported_functions: u.int_in_range(0..=MAX_IMPORTS)?,
|
|
||||||
imported_tables: u.int_in_range(0..=MAX_IMPORTS)?,
|
|
||||||
imported_memories: u.int_in_range(0..=MAX_IMPORTS)?,
|
|
||||||
imported_globals: u.int_in_range(0..=MAX_IMPORTS)?,
|
|
||||||
types: u.int_in_range(0..=MAX_TYPES)?,
|
|
||||||
functions: u.int_in_range(0..=MAX_FUNCTIONS)?,
|
|
||||||
tables: u.int_in_range(0..=MAX_TABLES)?,
|
|
||||||
memories: u.int_in_range(0..=MAX_MEMORIES)?,
|
|
||||||
globals: u.int_in_range(0..=MAX_GLOBALS)?,
|
|
||||||
table_elements: u.int_in_range(0..=MAX_ELEMENTS)?,
|
|
||||||
memory_pages: u.int_in_range(0..=MAX_MEMORY_PAGES)?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Configuration for `wasmtime::PoolingAllocationStrategy`.
|
/// Configuration for `wasmtime::PoolingAllocationStrategy`.
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||||
|
#[allow(missing_docs)]
|
||||||
pub struct InstanceLimits {
|
pub struct InstanceLimits {
|
||||||
/// The maximum number of instances that can be instantiated in the pool at a time.
|
|
||||||
pub count: u32,
|
pub count: u32,
|
||||||
|
pub memories: u32,
|
||||||
|
pub tables: u32,
|
||||||
|
pub memory_pages: u64,
|
||||||
|
pub table_elements: u32,
|
||||||
|
pub size: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InstanceLimits {
|
impl InstanceLimits {
|
||||||
fn to_wasmtime(&self) -> wasmtime::InstanceLimits {
|
fn to_wasmtime(&self) -> wasmtime::InstanceLimits {
|
||||||
wasmtime::InstanceLimits { count: self.count }
|
wasmtime::InstanceLimits {
|
||||||
|
count: self.count,
|
||||||
|
memories: self.memories,
|
||||||
|
tables: self.tables,
|
||||||
|
memory_pages: self.memory_pages,
|
||||||
|
table_elements: self.table_elements,
|
||||||
|
size: self.size,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,8 +89,19 @@ impl<'a> Arbitrary<'a> for InstanceLimits {
|
|||||||
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
|
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
|
||||||
const MAX_COUNT: u32 = 100;
|
const MAX_COUNT: u32 = 100;
|
||||||
|
|
||||||
|
const MAX_TABLES: u32 = 10;
|
||||||
|
const MAX_MEMORIES: u32 = 10;
|
||||||
|
const MAX_ELEMENTS: u32 = 1000;
|
||||||
|
const MAX_MEMORY_PAGES: u64 = 160; // 10 MiB
|
||||||
|
const MAX_SIZE: usize = 1 << 20; // 1 MiB
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
|
tables: u.int_in_range(0..=MAX_TABLES)?,
|
||||||
|
memories: u.int_in_range(0..=MAX_MEMORIES)?,
|
||||||
|
table_elements: u.int_in_range(0..=MAX_ELEMENTS)?,
|
||||||
|
memory_pages: u.int_in_range(0..=MAX_MEMORY_PAGES)?,
|
||||||
count: u.int_in_range(1..=MAX_COUNT)?,
|
count: u.int_in_range(1..=MAX_COUNT)?,
|
||||||
|
size: u.int_in_range(0..=MAX_SIZE)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -155,8 +115,6 @@ pub enum InstanceAllocationStrategy {
|
|||||||
Pooling {
|
Pooling {
|
||||||
/// The pooling strategy to use.
|
/// The pooling strategy to use.
|
||||||
strategy: PoolingAllocationStrategy,
|
strategy: PoolingAllocationStrategy,
|
||||||
/// The module limits.
|
|
||||||
module_limits: ModuleLimits,
|
|
||||||
/// The instance limits.
|
/// The instance limits.
|
||||||
instance_limits: InstanceLimits,
|
instance_limits: InstanceLimits,
|
||||||
},
|
},
|
||||||
@@ -168,11 +126,9 @@ impl InstanceAllocationStrategy {
|
|||||||
InstanceAllocationStrategy::OnDemand => wasmtime::InstanceAllocationStrategy::OnDemand,
|
InstanceAllocationStrategy::OnDemand => wasmtime::InstanceAllocationStrategy::OnDemand,
|
||||||
InstanceAllocationStrategy::Pooling {
|
InstanceAllocationStrategy::Pooling {
|
||||||
strategy,
|
strategy,
|
||||||
module_limits,
|
|
||||||
instance_limits,
|
instance_limits,
|
||||||
} => wasmtime::InstanceAllocationStrategy::Pooling {
|
} => wasmtime::InstanceAllocationStrategy::Pooling {
|
||||||
strategy: strategy.to_wasmtime(),
|
strategy: strategy.to_wasmtime(),
|
||||||
module_limits: module_limits.to_wasmtime(),
|
|
||||||
instance_limits: instance_limits.to_wasmtime(),
|
instance_limits: instance_limits.to_wasmtime(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -203,7 +159,7 @@ impl<'a> Arbitrary<'a> for Config {
|
|||||||
// If using the pooling allocator, constrain the memory and module configurations
|
// If using the pooling allocator, constrain the memory and module configurations
|
||||||
// to the module limits.
|
// to the module limits.
|
||||||
if let InstanceAllocationStrategy::Pooling {
|
if let InstanceAllocationStrategy::Pooling {
|
||||||
module_limits: limits,
|
instance_limits: limits,
|
||||||
..
|
..
|
||||||
} = &config.wasmtime.strategy
|
} = &config.wasmtime.strategy
|
||||||
{
|
{
|
||||||
@@ -223,14 +179,6 @@ impl<'a> Arbitrary<'a> for Config {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let cfg = &mut config.module_config.config;
|
let cfg = &mut config.module_config.config;
|
||||||
cfg.max_imports = limits.imported_functions.min(
|
|
||||||
limits
|
|
||||||
.imported_globals
|
|
||||||
.min(limits.imported_memories.min(limits.imported_tables)),
|
|
||||||
) as usize;
|
|
||||||
cfg.max_types = limits.types as usize;
|
|
||||||
cfg.max_funcs = limits.functions as usize;
|
|
||||||
cfg.max_globals = limits.globals as usize;
|
|
||||||
cfg.max_memories = limits.memories as usize;
|
cfg.max_memories = limits.memories as usize;
|
||||||
cfg.max_tables = limits.tables as usize;
|
cfg.max_tables = limits.tables as usize;
|
||||||
cfg.max_memory_pages = limits.memory_pages;
|
cfg.max_memory_pages = limits.memory_pages;
|
||||||
@@ -343,21 +291,13 @@ impl Config {
|
|||||||
config.simd_enabled = false;
|
config.simd_enabled = false;
|
||||||
config.memory64_enabled = false;
|
config.memory64_enabled = false;
|
||||||
|
|
||||||
// If using the pooling allocator, update the module limits too
|
// If using the pooling allocator, update the instance limits too
|
||||||
if let InstanceAllocationStrategy::Pooling {
|
if let InstanceAllocationStrategy::Pooling {
|
||||||
module_limits: limits,
|
instance_limits: limits,
|
||||||
..
|
..
|
||||||
} = &mut self.wasmtime.strategy
|
} = &mut self.wasmtime.strategy
|
||||||
{
|
{
|
||||||
// No imports
|
// One single-page memory
|
||||||
limits.imported_functions = 0;
|
|
||||||
limits.imported_tables = 0;
|
|
||||||
limits.imported_memories = 0;
|
|
||||||
limits.imported_globals = 0;
|
|
||||||
|
|
||||||
// One type, one function, and one single-page memory
|
|
||||||
limits.types = 1;
|
|
||||||
limits.functions = 1;
|
|
||||||
limits.memories = 1;
|
limits.memories = 1;
|
||||||
limits.memory_pages = 1;
|
limits.memory_pages = 1;
|
||||||
|
|
||||||
@@ -385,13 +325,6 @@ impl Config {
|
|||||||
|
|
||||||
if let Some(default_fuel) = default_fuel {
|
if let Some(default_fuel) = default_fuel {
|
||||||
module.ensure_termination(default_fuel);
|
module.ensure_termination(default_fuel);
|
||||||
|
|
||||||
// Bump the allowed global count by 1
|
|
||||||
if let InstanceAllocationStrategy::Pooling { module_limits, .. } =
|
|
||||||
&mut self.wasmtime.strategy
|
|
||||||
{
|
|
||||||
module_limits.globals += 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(module)
|
Ok(module)
|
||||||
@@ -408,7 +341,7 @@ impl Config {
|
|||||||
config.max_memories = 1;
|
config.max_memories = 1;
|
||||||
|
|
||||||
if let InstanceAllocationStrategy::Pooling {
|
if let InstanceAllocationStrategy::Pooling {
|
||||||
module_limits: limits,
|
instance_limits: limits,
|
||||||
..
|
..
|
||||||
} = &mut self.wasmtime.strategy
|
} = &mut self.wasmtime.strategy
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -23,9 +23,7 @@ use wasmtime_environ::{
|
|||||||
mod pooling;
|
mod pooling;
|
||||||
|
|
||||||
#[cfg(feature = "pooling-allocator")]
|
#[cfg(feature = "pooling-allocator")]
|
||||||
pub use self::pooling::{
|
pub use self::pooling::{InstanceLimits, PoolingAllocationStrategy, PoolingInstanceAllocator};
|
||||||
InstanceLimits, ModuleLimits, PoolingAllocationStrategy, PoolingInstanceAllocator,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Represents a request for a new runtime instance.
|
/// Represents a request for a new runtime instance.
|
||||||
pub struct InstanceAllocationRequest<'a> {
|
pub struct InstanceAllocationRequest<'a> {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -96,7 +96,7 @@ pub fn decommit_stack_pages(addr: *mut u8, len: usize) -> Result<()> {
|
|||||||
/// the page fault handler will detect an out of bounds access and treat the page, temporarily,
|
/// the page fault handler will detect an out of bounds access and treat the page, temporarily,
|
||||||
/// as a guard page.
|
/// as a guard page.
|
||||||
pub(super) fn initialize_memory_pool(pool: &MemoryPool) -> Result<()> {
|
pub(super) fn initialize_memory_pool(pool: &MemoryPool) -> Result<()> {
|
||||||
if pool.memory_size == 0 || pool.max_wasm_pages == 0 {
|
if pool.memory_reservation_size == 0 || pool.max_memory_size == 0 {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,7 +105,7 @@ pub(super) fn initialize_memory_pool(pool: &MemoryPool) -> Result<()> {
|
|||||||
unsafe {
|
unsafe {
|
||||||
region::protect(
|
region::protect(
|
||||||
base as _,
|
base as _,
|
||||||
pool.max_wasm_pages as usize * WASM_PAGE_SIZE,
|
pool.max_memory_size,
|
||||||
region::Protection::READ_WRITE,
|
region::Protection::READ_WRITE,
|
||||||
)
|
)
|
||||||
.context("failed to initialize memory pool for uffd")?;
|
.context("failed to initialize memory pool for uffd")?;
|
||||||
@@ -177,7 +177,7 @@ impl FaultLocator {
|
|||||||
max_instances: instances.max_instances,
|
max_instances: instances.max_instances,
|
||||||
memories_start,
|
memories_start,
|
||||||
memories_end,
|
memories_end,
|
||||||
memory_size: instances.memories.memory_size,
|
memory_size: instances.memories.memory_reservation_size,
|
||||||
max_memories: instances.memories.max_memories,
|
max_memories: instances.memories.max_memories,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -438,8 +438,8 @@ impl Drop for PageFaultHandler {
|
|||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
Imports, InstanceAllocationRequest, InstanceLimits, ModuleLimits,
|
Imports, InstanceAllocationRequest, InstanceLimits, PoolingAllocationStrategy, Store,
|
||||||
PoolingAllocationStrategy, Store, StorePtr,
|
StorePtr,
|
||||||
};
|
};
|
||||||
use std::sync::atomic::AtomicU64;
|
use std::sync::atomic::AtomicU64;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@@ -448,20 +448,15 @@ mod test {
|
|||||||
#[cfg(target_pointer_width = "64")]
|
#[cfg(target_pointer_width = "64")]
|
||||||
#[test]
|
#[test]
|
||||||
fn test_address_locator() {
|
fn test_address_locator() {
|
||||||
let module_limits = ModuleLimits {
|
let instance_limits = InstanceLimits {
|
||||||
imported_functions: 0,
|
count: 3,
|
||||||
imported_tables: 0,
|
|
||||||
imported_memories: 0,
|
|
||||||
imported_globals: 0,
|
|
||||||
types: 0,
|
|
||||||
functions: 0,
|
|
||||||
tables: 0,
|
tables: 0,
|
||||||
memories: 2,
|
memories: 2,
|
||||||
globals: 0,
|
|
||||||
table_elements: 0,
|
table_elements: 0,
|
||||||
memory_pages: 2,
|
memory_pages: 2,
|
||||||
|
size: 1000,
|
||||||
|
..Default::default()
|
||||||
};
|
};
|
||||||
let instance_limits = InstanceLimits { count: 3 };
|
|
||||||
let tunables = Tunables {
|
let tunables = Tunables {
|
||||||
static_memory_bound: 10,
|
static_memory_bound: 10,
|
||||||
static_memory_offset_guard_size: 0,
|
static_memory_offset_guard_size: 0,
|
||||||
@@ -471,7 +466,6 @@ mod test {
|
|||||||
|
|
||||||
let instances = InstancePool::new(
|
let instances = InstancePool::new(
|
||||||
PoolingAllocationStrategy::Random,
|
PoolingAllocationStrategy::Random,
|
||||||
&module_limits,
|
|
||||||
&instance_limits,
|
&instance_limits,
|
||||||
&tunables,
|
&tunables,
|
||||||
)
|
)
|
||||||
@@ -480,7 +474,7 @@ mod test {
|
|||||||
let locator = FaultLocator::new(&instances);
|
let locator = FaultLocator::new(&instances);
|
||||||
|
|
||||||
assert_eq!(locator.instances_start, instances.mapping.as_ptr() as usize);
|
assert_eq!(locator.instances_start, instances.mapping.as_ptr() as usize);
|
||||||
assert_eq!(locator.instance_size, 4096);
|
assert_eq!(locator.instance_size, 1008);
|
||||||
assert_eq!(locator.max_instances, 3);
|
assert_eq!(locator.max_instances, 3);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
locator.memories_start,
|
locator.memories_start,
|
||||||
@@ -499,7 +493,7 @@ mod test {
|
|||||||
|
|
||||||
let mut module = Module::new();
|
let mut module = Module::new();
|
||||||
|
|
||||||
for _ in 0..module_limits.memories {
|
for _ in 0..instance_limits.memories {
|
||||||
module.memory_plans.push(MemoryPlan {
|
module.memory_plans.push(MemoryPlan {
|
||||||
memory: Memory {
|
memory: Memory {
|
||||||
minimum: 2,
|
minimum: 2,
|
||||||
@@ -513,8 +507,6 @@ mod test {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
module_limits.validate(&module).expect("should validate");
|
|
||||||
|
|
||||||
// An InstanceAllocationRequest with a module must also have
|
// An InstanceAllocationRequest with a module must also have
|
||||||
// a non-null StorePtr. Here we mock just enough of a store
|
// a non-null StorePtr. Here we mock just enough of a store
|
||||||
// to satisfy this test.
|
// to satisfy this test.
|
||||||
|
|||||||
@@ -54,9 +54,7 @@ pub use crate::instance::{
|
|||||||
OnDemandInstanceAllocator, StorePtr,
|
OnDemandInstanceAllocator, StorePtr,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "pooling-allocator")]
|
#[cfg(feature = "pooling-allocator")]
|
||||||
pub use crate::instance::{
|
pub use crate::instance::{InstanceLimits, PoolingAllocationStrategy, PoolingInstanceAllocator};
|
||||||
InstanceLimits, ModuleLimits, PoolingAllocationStrategy, PoolingInstanceAllocator,
|
|
||||||
};
|
|
||||||
pub use crate::memory::{DefaultMemoryCreator, Memory, RuntimeLinearMemory, RuntimeMemoryCreator};
|
pub use crate::memory::{DefaultMemoryCreator, Memory, RuntimeLinearMemory, RuntimeMemoryCreator};
|
||||||
pub use crate::mmap::Mmap;
|
pub use crate::mmap::Mmap;
|
||||||
pub use crate::mmap_vec::MmapVec;
|
pub use crate::mmap_vec::MmapVec;
|
||||||
|
|||||||
@@ -315,6 +315,15 @@ impl Memory {
|
|||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
let (minimum, maximum) = Self::limit_new(plan, store)?;
|
let (minimum, maximum) = Self::limit_new(plan, store)?;
|
||||||
|
|
||||||
|
if base.len() < minimum {
|
||||||
|
bail!(
|
||||||
|
"initial memory size of {} exceeds the pooling allocator's \
|
||||||
|
configured maximum memory size of {} bytes",
|
||||||
|
minimum,
|
||||||
|
base.len(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let base = match maximum {
|
let base = match maximum {
|
||||||
Some(max) if max < base.len() => &mut base[..max],
|
Some(max) if max < base.len() => &mut base[..max],
|
||||||
_ => base,
|
_ => base,
|
||||||
|
|||||||
@@ -195,6 +195,14 @@ impl Table {
|
|||||||
Self::limit_new(plan, store)?;
|
Self::limit_new(plan, store)?;
|
||||||
let size = plan.table.minimum;
|
let size = plan.table.minimum;
|
||||||
let ty = wasm_to_table_type(plan.table.wasm_ty)?;
|
let ty = wasm_to_table_type(plan.table.wasm_ty)?;
|
||||||
|
if data.len() < (plan.table.minimum as usize) {
|
||||||
|
bail!(
|
||||||
|
"initial table size of {} exceeds the pooling allocator's \
|
||||||
|
configured maximum table size of {} elements",
|
||||||
|
plan.table.minimum,
|
||||||
|
data.len(),
|
||||||
|
);
|
||||||
|
}
|
||||||
let data = match plan.table.maximum {
|
let data = match plan.table.maximum {
|
||||||
Some(max) if (max as usize) < data.len() => &mut data[..max as usize],
|
Some(max) if (max as usize) < data.len() => &mut data[..max as usize],
|
||||||
_ => data,
|
_ => data,
|
||||||
|
|||||||
@@ -15,10 +15,7 @@ use wasmtime_jit::{JitDumpAgent, NullProfilerAgent, ProfilingAgent, VTuneAgent};
|
|||||||
use wasmtime_runtime::{InstanceAllocator, OnDemandInstanceAllocator, RuntimeMemoryCreator};
|
use wasmtime_runtime::{InstanceAllocator, OnDemandInstanceAllocator, RuntimeMemoryCreator};
|
||||||
|
|
||||||
#[cfg(feature = "pooling-allocator")]
|
#[cfg(feature = "pooling-allocator")]
|
||||||
mod pooling;
|
pub use wasmtime_runtime::{InstanceLimits, PoolingAllocationStrategy};
|
||||||
|
|
||||||
#[cfg(feature = "pooling-allocator")]
|
|
||||||
pub use self::pooling::*;
|
|
||||||
|
|
||||||
/// Represents the module instance allocation strategy to use.
|
/// Represents the module instance allocation strategy to use.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@@ -39,8 +36,6 @@ pub enum InstanceAllocationStrategy {
|
|||||||
Pooling {
|
Pooling {
|
||||||
/// The allocation strategy to use.
|
/// The allocation strategy to use.
|
||||||
strategy: PoolingAllocationStrategy,
|
strategy: PoolingAllocationStrategy,
|
||||||
/// The module limits to use.
|
|
||||||
module_limits: ModuleLimits,
|
|
||||||
/// The instance limits to use.
|
/// The instance limits to use.
|
||||||
instance_limits: InstanceLimits,
|
instance_limits: InstanceLimits,
|
||||||
},
|
},
|
||||||
@@ -52,7 +47,6 @@ impl InstanceAllocationStrategy {
|
|||||||
pub fn pooling() -> Self {
|
pub fn pooling() -> Self {
|
||||||
Self::Pooling {
|
Self::Pooling {
|
||||||
strategy: PoolingAllocationStrategy::default(),
|
strategy: PoolingAllocationStrategy::default(),
|
||||||
module_limits: ModuleLimits::default(),
|
|
||||||
instance_limits: InstanceLimits::default(),
|
instance_limits: InstanceLimits::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1281,12 +1275,10 @@ impl Config {
|
|||||||
#[cfg(feature = "pooling-allocator")]
|
#[cfg(feature = "pooling-allocator")]
|
||||||
InstanceAllocationStrategy::Pooling {
|
InstanceAllocationStrategy::Pooling {
|
||||||
strategy,
|
strategy,
|
||||||
module_limits,
|
|
||||||
instance_limits,
|
instance_limits,
|
||||||
} => Ok(Box::new(wasmtime_runtime::PoolingInstanceAllocator::new(
|
} => Ok(Box::new(wasmtime_runtime::PoolingInstanceAllocator::new(
|
||||||
strategy.into(),
|
strategy,
|
||||||
module_limits.into(),
|
instance_limits,
|
||||||
instance_limits.into(),
|
|
||||||
stack_size,
|
stack_size,
|
||||||
&self.tunables,
|
&self.tunables,
|
||||||
)?)),
|
)?)),
|
||||||
|
|||||||
@@ -1,279 +0,0 @@
|
|||||||
//! This module contains types exposed via `Config` relating to the pooling allocator feature.
|
|
||||||
|
|
||||||
/// Represents the limits placed on a module for compiling with the pooling instance allocation strategy.
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
|
||||||
pub struct ModuleLimits {
|
|
||||||
/// The maximum number of imported functions for a module (default is 1000).
|
|
||||||
///
|
|
||||||
/// This value controls the capacity of the `VMFunctionImport` table and the
|
|
||||||
/// `VMCallerCheckedAnyfunc` table in each instance's `VMContext` structure.
|
|
||||||
///
|
|
||||||
/// The allocated size of the `VMFunctionImport` table will be `imported_functions * sizeof(VMFunctionImport)`
|
|
||||||
/// for each instance regardless of how many functions an instance imports.
|
|
||||||
///
|
|
||||||
/// The allocated size of the `VMCallerCheckedAnyfunc` table will be
|
|
||||||
/// `imported_functions * functions * sizeof(VMCallerCheckedAnyfunc)` for each instance regardless of
|
|
||||||
/// how many functions are imported and defined by an instance.
|
|
||||||
pub imported_functions: u32,
|
|
||||||
|
|
||||||
/// The maximum number of imported tables for a module (default is 0).
|
|
||||||
///
|
|
||||||
/// This value controls the capacity of the `VMTableImport` table in each instance's
|
|
||||||
/// `VMContext` structure.
|
|
||||||
///
|
|
||||||
/// The allocated size of the table will be `imported_tables * sizeof(VMTableImport)` for each
|
|
||||||
/// instance regardless of how many tables an instance imports.
|
|
||||||
pub imported_tables: u32,
|
|
||||||
|
|
||||||
/// The maximum number of imported linear memories for a module (default is 0).
|
|
||||||
///
|
|
||||||
/// This value controls the capacity of the `VMMemoryImport` table in each instance's
|
|
||||||
/// `VMContext` structure.
|
|
||||||
///
|
|
||||||
/// The allocated size of the table will be `imported_memories * sizeof(VMMemoryImport)` for each
|
|
||||||
/// instance regardless of how many memories an instance imports.
|
|
||||||
pub imported_memories: u32,
|
|
||||||
|
|
||||||
/// The maximum number of imported globals for a module (default is 0).
|
|
||||||
///
|
|
||||||
/// This value controls the capacity of the `VMGlobalImport` table in each instance's
|
|
||||||
/// `VMContext` structure.
|
|
||||||
///
|
|
||||||
/// The allocated size of the table will be `imported_globals * sizeof(VMGlobalImport)` for each
|
|
||||||
/// instance regardless of how many globals an instance imports.
|
|
||||||
pub imported_globals: u32,
|
|
||||||
|
|
||||||
/// The maximum number of defined types for a module (default is 100).
|
|
||||||
///
|
|
||||||
/// This value controls the capacity of the `VMSharedSignatureIndex` table in each instance's
|
|
||||||
/// `VMContext` structure.
|
|
||||||
///
|
|
||||||
/// The allocated size of the table will be `types * sizeof(VMSharedSignatureIndex)` for each
|
|
||||||
/// instance regardless of how many types are defined by an instance's module.
|
|
||||||
pub types: u32,
|
|
||||||
|
|
||||||
/// The maximum number of defined functions for a module (default is 10000).
|
|
||||||
///
|
|
||||||
/// This value controls the capacity of the `VMCallerCheckedAnyfunc` table in each instance's
|
|
||||||
/// `VMContext` structure.
|
|
||||||
///
|
|
||||||
/// The allocated size of the `VMCallerCheckedAnyfunc` table will be
|
|
||||||
/// `imported_functions * functions * sizeof(VMCallerCheckedAnyfunc)` for each instance
|
|
||||||
/// regardless of how many functions are imported and defined by an instance.
|
|
||||||
pub functions: u32,
|
|
||||||
|
|
||||||
/// The maximum number of defined tables for a module (default is 1).
|
|
||||||
///
|
|
||||||
/// This value controls the capacity of the `VMTableDefinition` table in each instance's
|
|
||||||
/// `VMContext` structure.
|
|
||||||
///
|
|
||||||
/// The allocated size of the table will be `tables * sizeof(VMTableDefinition)` for each
|
|
||||||
/// instance regardless of how many tables are defined by an instance's module.
|
|
||||||
pub tables: u32,
|
|
||||||
|
|
||||||
/// The maximum number of defined linear memories for a module (default is 1).
|
|
||||||
///
|
|
||||||
/// This value controls the capacity of the `VMMemoryDefinition` table in each instance's
|
|
||||||
/// `VMContext` structure.
|
|
||||||
///
|
|
||||||
/// The allocated size of the table will be `memories * sizeof(VMMemoryDefinition)` for each
|
|
||||||
/// instance regardless of how many memories are defined by an instance's module.
|
|
||||||
pub memories: u32,
|
|
||||||
|
|
||||||
/// The maximum number of defined globals for a module (default is 10).
|
|
||||||
///
|
|
||||||
/// This value controls the capacity of the `VMGlobalDefinition` table in each instance's
|
|
||||||
/// `VMContext` structure.
|
|
||||||
///
|
|
||||||
/// The allocated size of the table will be `globals * sizeof(VMGlobalDefinition)` for each
|
|
||||||
/// instance regardless of how many globals are defined by an instance's module.
|
|
||||||
pub globals: u32,
|
|
||||||
|
|
||||||
/// The maximum table elements for any table defined in a module (default is 10000).
|
|
||||||
///
|
|
||||||
/// If a table's minimum element limit is greater than this value, the module will
|
|
||||||
/// fail to compile.
|
|
||||||
///
|
|
||||||
/// If a table's maximum element limit is unbounded or greater than this value,
|
|
||||||
/// the maximum will be `table_elements` for the purpose of any `table.grow` instruction.
|
|
||||||
///
|
|
||||||
/// This value is used to reserve the maximum space for each supported table; table elements
|
|
||||||
/// are pointer-sized in the Wasmtime runtime. Therefore, the space reserved for each instance
|
|
||||||
/// is `tables * table_elements * sizeof::<*const ()>`.
|
|
||||||
pub table_elements: u32,
|
|
||||||
|
|
||||||
/// The maximum number of pages for any linear memory defined in a module (default is 160).
|
|
||||||
///
|
|
||||||
/// The default of 160 means at most 10 MiB of host memory may be committed for each instance.
|
|
||||||
///
|
|
||||||
/// If a memory's minimum page limit is greater than this value, the module will
|
|
||||||
/// fail to compile.
|
|
||||||
///
|
|
||||||
/// If a memory's maximum page limit is unbounded or greater than this value,
|
|
||||||
/// the maximum will be `memory_pages` for the purpose of any `memory.grow` instruction.
|
|
||||||
///
|
|
||||||
/// This value is used to control the maximum accessible space for each linear memory of an instance.
|
|
||||||
///
|
|
||||||
/// The reservation size of each linear memory is controlled by the
|
|
||||||
/// [`static_memory_maximum_size`](super::Config::static_memory_maximum_size) setting and this value cannot
|
|
||||||
/// exceed the configured static memory maximum size.
|
|
||||||
pub memory_pages: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for ModuleLimits {
|
|
||||||
fn default() -> Self {
|
|
||||||
// Use the defaults from the runtime
|
|
||||||
let wasmtime_runtime::ModuleLimits {
|
|
||||||
imported_functions,
|
|
||||||
imported_tables,
|
|
||||||
imported_memories,
|
|
||||||
imported_globals,
|
|
||||||
types,
|
|
||||||
functions,
|
|
||||||
tables,
|
|
||||||
memories,
|
|
||||||
globals,
|
|
||||||
table_elements,
|
|
||||||
memory_pages,
|
|
||||||
} = wasmtime_runtime::ModuleLimits::default();
|
|
||||||
|
|
||||||
Self {
|
|
||||||
imported_functions,
|
|
||||||
imported_tables,
|
|
||||||
imported_memories,
|
|
||||||
imported_globals,
|
|
||||||
types,
|
|
||||||
functions,
|
|
||||||
tables,
|
|
||||||
memories,
|
|
||||||
globals,
|
|
||||||
table_elements,
|
|
||||||
memory_pages,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This exists so we can convert between the public Wasmtime API and the runtime representation
|
|
||||||
// without having to export runtime types from the Wasmtime API.
|
|
||||||
#[doc(hidden)]
|
|
||||||
impl Into<wasmtime_runtime::ModuleLimits> for ModuleLimits {
|
|
||||||
fn into(self) -> wasmtime_runtime::ModuleLimits {
|
|
||||||
let Self {
|
|
||||||
imported_functions,
|
|
||||||
imported_tables,
|
|
||||||
imported_memories,
|
|
||||||
imported_globals,
|
|
||||||
types,
|
|
||||||
functions,
|
|
||||||
tables,
|
|
||||||
memories,
|
|
||||||
globals,
|
|
||||||
table_elements,
|
|
||||||
memory_pages,
|
|
||||||
} = self;
|
|
||||||
|
|
||||||
wasmtime_runtime::ModuleLimits {
|
|
||||||
imported_functions,
|
|
||||||
imported_tables,
|
|
||||||
imported_memories,
|
|
||||||
imported_globals,
|
|
||||||
types,
|
|
||||||
functions,
|
|
||||||
tables,
|
|
||||||
memories,
|
|
||||||
globals,
|
|
||||||
table_elements,
|
|
||||||
memory_pages,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Represents the limits placed on instances by the pooling instance allocation strategy.
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
|
||||||
pub struct InstanceLimits {
|
|
||||||
/// The maximum number of concurrent instances supported (default is 1000).
|
|
||||||
///
|
|
||||||
/// This value has a direct impact on the amount of memory allocated by the pooling
|
|
||||||
/// instance allocator.
|
|
||||||
///
|
|
||||||
/// The pooling instance allocator allocates three memory pools with sizes depending on this value:
|
|
||||||
///
|
|
||||||
/// * An instance pool, where each entry in the pool can store the runtime representation
|
|
||||||
/// of an instance, including a maximal `VMContext` structure (see [`ModuleLimits`](ModuleLimits)
|
|
||||||
/// for the various settings that control the size of each instance's `VMContext` structure).
|
|
||||||
///
|
|
||||||
/// * A memory pool, where each entry in the pool contains the reserved address space for each
|
|
||||||
/// linear memory supported by an instance.
|
|
||||||
///
|
|
||||||
/// * A table pool, where each entry in the pool contains the space needed for each WebAssembly table
|
|
||||||
/// supported by an instance (see `[ModuleLimits::table_elements`] to control the size of each table).
|
|
||||||
///
|
|
||||||
/// Additionally, this value will also control the maximum number of execution stacks allowed for
|
|
||||||
/// asynchronous execution (one per instance), when enabled.
|
|
||||||
///
|
|
||||||
/// The memory pool will reserve a large quantity of host process address space to elide the bounds
|
|
||||||
/// checks required for correct WebAssembly memory semantics. Even for 64-bit address spaces, the
|
|
||||||
/// address space is limited when dealing with a large number of supported instances.
|
|
||||||
///
|
|
||||||
/// For example, on Linux x86_64, the userland address space limit is 128 TiB. That might seem like a lot,
|
|
||||||
/// but each linear memory will *reserve* 6 GiB of space by default. Multiply that by the number of linear
|
|
||||||
/// memories each instance supports and then by the number of supported instances and it becomes apparent
|
|
||||||
/// that address space can be exhausted depending on the number of supported instances.
|
|
||||||
pub count: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for InstanceLimits {
|
|
||||||
fn default() -> Self {
|
|
||||||
let wasmtime_runtime::InstanceLimits { count } =
|
|
||||||
wasmtime_runtime::InstanceLimits::default();
|
|
||||||
|
|
||||||
Self { count }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This exists so we can convert between the public Wasmtime API and the runtime representation
|
|
||||||
// without having to export runtime types from the Wasmtime API.
|
|
||||||
#[doc(hidden)]
|
|
||||||
impl Into<wasmtime_runtime::InstanceLimits> for InstanceLimits {
|
|
||||||
fn into(self) -> wasmtime_runtime::InstanceLimits {
|
|
||||||
let Self { count } = self;
|
|
||||||
|
|
||||||
wasmtime_runtime::InstanceLimits { count }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The allocation strategy to use for the pooling instance allocation strategy.
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
||||||
pub enum PoolingAllocationStrategy {
|
|
||||||
/// Allocate from the next available instance.
|
|
||||||
NextAvailable,
|
|
||||||
/// Allocate from a random available instance.
|
|
||||||
Random,
|
|
||||||
/// Try to allocate an instance slot that was previously used for
|
|
||||||
/// the same module, potentially enabling faster instantiation by
|
|
||||||
/// reusing e.g. memory mappings.
|
|
||||||
ReuseAffinity,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for PoolingAllocationStrategy {
|
|
||||||
fn default() -> Self {
|
|
||||||
match wasmtime_runtime::PoolingAllocationStrategy::default() {
|
|
||||||
wasmtime_runtime::PoolingAllocationStrategy::NextAvailable => Self::NextAvailable,
|
|
||||||
wasmtime_runtime::PoolingAllocationStrategy::Random => Self::Random,
|
|
||||||
wasmtime_runtime::PoolingAllocationStrategy::ReuseAffinity => Self::ReuseAffinity,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This exists so we can convert between the public Wasmtime API and the runtime representation
|
|
||||||
// without having to export runtime types from the Wasmtime API.
|
|
||||||
#[doc(hidden)]
|
|
||||||
impl Into<wasmtime_runtime::PoolingAllocationStrategy> for PoolingAllocationStrategy {
|
|
||||||
fn into(self) -> wasmtime_runtime::PoolingAllocationStrategy {
|
|
||||||
match self {
|
|
||||||
Self::NextAvailable => wasmtime_runtime::PoolingAllocationStrategy::NextAvailable,
|
|
||||||
Self::Random => wasmtime_runtime::PoolingAllocationStrategy::Random,
|
|
||||||
Self::ReuseAffinity => wasmtime_runtime::PoolingAllocationStrategy::ReuseAffinity,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -538,8 +538,10 @@ impl Module {
|
|||||||
types: Arc<TypeTables>,
|
types: Arc<TypeTables>,
|
||||||
module_upvars: &[serialization::SerializedModuleUpvar],
|
module_upvars: &[serialization::SerializedModuleUpvar],
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
// Validate the module can be used with the current allocator
|
// Validate all modules can be used with the current allocator
|
||||||
engine.allocator().validate(modules[main_module].module())?;
|
for module in modules.iter() {
|
||||||
|
engine.allocator().validate(module.module())?;
|
||||||
|
}
|
||||||
|
|
||||||
let signatures = Arc::new(SignatureCollection::new_for_module(
|
let signatures = Arc::new(SignatureCollection::new_for_module(
|
||||||
engine.signatures(),
|
engine.signatures(),
|
||||||
@@ -564,7 +566,7 @@ impl Module {
|
|||||||
&signatures,
|
&signatures,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect::<Result<Vec<_>>>()?;
|
.collect();
|
||||||
|
|
||||||
return Ok(Self {
|
return Ok(Self {
|
||||||
inner: Arc::new(ModuleInner {
|
inner: Arc::new(ModuleInner {
|
||||||
@@ -586,9 +588,9 @@ impl Module {
|
|||||||
artifact_upvars: &[usize],
|
artifact_upvars: &[usize],
|
||||||
module_upvars: &[serialization::SerializedModuleUpvar],
|
module_upvars: &[serialization::SerializedModuleUpvar],
|
||||||
signatures: &Arc<SignatureCollection>,
|
signatures: &Arc<SignatureCollection>,
|
||||||
) -> Result<Module> {
|
) -> Module {
|
||||||
let module = artifacts[module_index].clone();
|
let module = artifacts[module_index].clone();
|
||||||
Ok(Module {
|
Module {
|
||||||
inner: Arc::new(ModuleInner {
|
inner: Arc::new(ModuleInner {
|
||||||
engine: engine.clone(),
|
engine: engine.clone(),
|
||||||
types: types.clone(),
|
types: types.clone(),
|
||||||
@@ -611,10 +613,10 @@ impl Module {
|
|||||||
signatures,
|
signatures,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect::<Result<Vec<_>>>()?,
|
.collect(),
|
||||||
signatures: signatures.clone(),
|
signatures: signatures.clone(),
|
||||||
}),
|
}),
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ use std::path::PathBuf;
|
|||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
use wasmtime::{Config, ProfilingStrategy};
|
use wasmtime::{Config, ProfilingStrategy};
|
||||||
#[cfg(feature = "pooling-allocator")]
|
#[cfg(feature = "pooling-allocator")]
|
||||||
use wasmtime::{InstanceLimits, ModuleLimits, PoolingAllocationStrategy};
|
use wasmtime::{InstanceLimits, PoolingAllocationStrategy};
|
||||||
|
|
||||||
fn pick_profiling_strategy(jitdump: bool, vtune: bool) -> Result<ProfilingStrategy> {
|
fn pick_profiling_strategy(jitdump: bool, vtune: bool) -> Result<ProfilingStrategy> {
|
||||||
Ok(match (jitdump, vtune) {
|
Ok(match (jitdump, vtune) {
|
||||||
@@ -347,15 +347,9 @@ impl CommonOptions {
|
|||||||
#[cfg(feature = "pooling-allocator")]
|
#[cfg(feature = "pooling-allocator")]
|
||||||
{
|
{
|
||||||
if self.pooling_allocator {
|
if self.pooling_allocator {
|
||||||
let mut module_limits = ModuleLimits::default();
|
|
||||||
module_limits.functions = 50000;
|
|
||||||
module_limits.types = 10000;
|
|
||||||
module_limits.globals = 1000;
|
|
||||||
module_limits.memory_pages = 2048;
|
|
||||||
let instance_limits = InstanceLimits::default();
|
let instance_limits = InstanceLimits::default();
|
||||||
config.allocation_strategy(wasmtime::InstanceAllocationStrategy::Pooling {
|
config.allocation_strategy(wasmtime::InstanceAllocationStrategy::Pooling {
|
||||||
strategy: PoolingAllocationStrategy::NextAvailable,
|
strategy: PoolingAllocationStrategy::NextAvailable,
|
||||||
module_limits,
|
|
||||||
instance_limits,
|
instance_limits,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -425,12 +425,12 @@ fn async_with_pooling_stacks() {
|
|||||||
config.async_support(true);
|
config.async_support(true);
|
||||||
config.allocation_strategy(InstanceAllocationStrategy::Pooling {
|
config.allocation_strategy(InstanceAllocationStrategy::Pooling {
|
||||||
strategy: PoolingAllocationStrategy::NextAvailable,
|
strategy: PoolingAllocationStrategy::NextAvailable,
|
||||||
module_limits: ModuleLimits {
|
instance_limits: InstanceLimits {
|
||||||
|
count: 1,
|
||||||
memory_pages: 1,
|
memory_pages: 1,
|
||||||
table_elements: 0,
|
table_elements: 0,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
instance_limits: InstanceLimits { count: 1 },
|
|
||||||
});
|
});
|
||||||
config.dynamic_memory_guard_size(0);
|
config.dynamic_memory_guard_size(0);
|
||||||
config.static_memory_guard_size(0);
|
config.static_memory_guard_size(0);
|
||||||
@@ -454,12 +454,12 @@ fn async_host_func_with_pooling_stacks() -> Result<()> {
|
|||||||
config.async_support(true);
|
config.async_support(true);
|
||||||
config.allocation_strategy(InstanceAllocationStrategy::Pooling {
|
config.allocation_strategy(InstanceAllocationStrategy::Pooling {
|
||||||
strategy: PoolingAllocationStrategy::NextAvailable,
|
strategy: PoolingAllocationStrategy::NextAvailable,
|
||||||
module_limits: ModuleLimits {
|
instance_limits: InstanceLimits {
|
||||||
|
count: 1,
|
||||||
memory_pages: 1,
|
memory_pages: 1,
|
||||||
table_elements: 0,
|
table_elements: 0,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
instance_limits: InstanceLimits { count: 1 },
|
|
||||||
});
|
});
|
||||||
config.dynamic_memory_guard_size(0);
|
config.dynamic_memory_guard_size(0);
|
||||||
config.static_memory_guard_size(0);
|
config.static_memory_guard_size(0);
|
||||||
|
|||||||
@@ -66,11 +66,10 @@ fn linear_memory_limits() -> Result<()> {
|
|||||||
test(&Engine::new(Config::new().allocation_strategy(
|
test(&Engine::new(Config::new().allocation_strategy(
|
||||||
InstanceAllocationStrategy::Pooling {
|
InstanceAllocationStrategy::Pooling {
|
||||||
strategy: PoolingAllocationStrategy::NextAvailable,
|
strategy: PoolingAllocationStrategy::NextAvailable,
|
||||||
module_limits: ModuleLimits {
|
instance_limits: InstanceLimits {
|
||||||
memory_pages: 65536,
|
memory_pages: 65536,
|
||||||
..ModuleLimits::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
instance_limits: InstanceLimits::default(),
|
|
||||||
},
|
},
|
||||||
))?)?;
|
))?)?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
|||||||
@@ -354,12 +354,9 @@ fn test_pooling_allocator_initial_limits_exceeded() -> Result<()> {
|
|||||||
config.wasm_multi_memory(true);
|
config.wasm_multi_memory(true);
|
||||||
config.allocation_strategy(InstanceAllocationStrategy::Pooling {
|
config.allocation_strategy(InstanceAllocationStrategy::Pooling {
|
||||||
strategy: PoolingAllocationStrategy::NextAvailable,
|
strategy: PoolingAllocationStrategy::NextAvailable,
|
||||||
module_limits: ModuleLimits {
|
|
||||||
memories: 2,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
instance_limits: InstanceLimits {
|
instance_limits: InstanceLimits {
|
||||||
count: 1,
|
count: 1,
|
||||||
|
memories: 2,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -727,14 +724,11 @@ fn custom_limiter_detect_grow_failure() -> Result<()> {
|
|||||||
let mut config = Config::new();
|
let mut config = Config::new();
|
||||||
config.allocation_strategy(InstanceAllocationStrategy::Pooling {
|
config.allocation_strategy(InstanceAllocationStrategy::Pooling {
|
||||||
strategy: PoolingAllocationStrategy::NextAvailable,
|
strategy: PoolingAllocationStrategy::NextAvailable,
|
||||||
module_limits: ModuleLimits {
|
instance_limits: InstanceLimits {
|
||||||
memory_pages: 10,
|
memory_pages: 10,
|
||||||
table_elements: 10,
|
table_elements: 10,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
instance_limits: InstanceLimits {
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
let engine = Engine::new(&config).unwrap();
|
let engine = Engine::new(&config).unwrap();
|
||||||
let linker = Linker::new(&engine);
|
let linker = Linker::new(&engine);
|
||||||
@@ -839,14 +833,11 @@ async fn custom_limiter_async_detect_grow_failure() -> Result<()> {
|
|||||||
config.async_support(true);
|
config.async_support(true);
|
||||||
config.allocation_strategy(InstanceAllocationStrategy::Pooling {
|
config.allocation_strategy(InstanceAllocationStrategy::Pooling {
|
||||||
strategy: PoolingAllocationStrategy::NextAvailable,
|
strategy: PoolingAllocationStrategy::NextAvailable,
|
||||||
module_limits: ModuleLimits {
|
instance_limits: InstanceLimits {
|
||||||
memory_pages: 10,
|
memory_pages: 10,
|
||||||
table_elements: 10,
|
table_elements: 10,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
instance_limits: InstanceLimits {
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
let engine = Engine::new(&config).unwrap();
|
let engine = Engine::new(&config).unwrap();
|
||||||
let linker = Linker::new(&engine);
|
let linker = Linker::new(&engine);
|
||||||
|
|||||||
@@ -193,11 +193,11 @@ fn guards_present_pooling() -> Result<()> {
|
|||||||
config.guard_before_linear_memory(true);
|
config.guard_before_linear_memory(true);
|
||||||
config.allocation_strategy(InstanceAllocationStrategy::Pooling {
|
config.allocation_strategy(InstanceAllocationStrategy::Pooling {
|
||||||
strategy: PoolingAllocationStrategy::default(),
|
strategy: PoolingAllocationStrategy::default(),
|
||||||
module_limits: ModuleLimits {
|
instance_limits: InstanceLimits {
|
||||||
|
count: 2,
|
||||||
memory_pages: 10,
|
memory_pages: 10,
|
||||||
..ModuleLimits::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
instance_limits: InstanceLimits { count: 2 },
|
|
||||||
});
|
});
|
||||||
let engine = Engine::new(&config)?;
|
let engine = Engine::new(&config)?;
|
||||||
|
|
||||||
|
|||||||
@@ -7,12 +7,12 @@ fn successful_instantiation() -> Result<()> {
|
|||||||
let mut config = Config::new();
|
let mut config = Config::new();
|
||||||
config.allocation_strategy(InstanceAllocationStrategy::Pooling {
|
config.allocation_strategy(InstanceAllocationStrategy::Pooling {
|
||||||
strategy: PoolingAllocationStrategy::NextAvailable,
|
strategy: PoolingAllocationStrategy::NextAvailable,
|
||||||
module_limits: ModuleLimits {
|
instance_limits: InstanceLimits {
|
||||||
|
count: 1,
|
||||||
memory_pages: 1,
|
memory_pages: 1,
|
||||||
table_elements: 10,
|
table_elements: 10,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
instance_limits: InstanceLimits { count: 1 },
|
|
||||||
});
|
});
|
||||||
config.dynamic_memory_guard_size(0);
|
config.dynamic_memory_guard_size(0);
|
||||||
config.static_memory_guard_size(0);
|
config.static_memory_guard_size(0);
|
||||||
@@ -33,25 +33,36 @@ fn memory_limit() -> Result<()> {
|
|||||||
let mut config = Config::new();
|
let mut config = Config::new();
|
||||||
config.allocation_strategy(InstanceAllocationStrategy::Pooling {
|
config.allocation_strategy(InstanceAllocationStrategy::Pooling {
|
||||||
strategy: PoolingAllocationStrategy::NextAvailable,
|
strategy: PoolingAllocationStrategy::NextAvailable,
|
||||||
module_limits: ModuleLimits {
|
instance_limits: InstanceLimits {
|
||||||
|
count: 1,
|
||||||
memory_pages: 3,
|
memory_pages: 3,
|
||||||
table_elements: 10,
|
table_elements: 10,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
instance_limits: InstanceLimits { count: 1 },
|
|
||||||
});
|
});
|
||||||
config.dynamic_memory_guard_size(0);
|
config.dynamic_memory_guard_size(0);
|
||||||
config.static_memory_guard_size(65536);
|
config.static_memory_guard_size(65536);
|
||||||
config.static_memory_maximum_size(3 * 65536);
|
config.static_memory_maximum_size(3 * 65536);
|
||||||
|
config.wasm_multi_memory(true);
|
||||||
|
|
||||||
let engine = Engine::new(&config)?;
|
let engine = Engine::new(&config)?;
|
||||||
|
|
||||||
// Module should fail to validate because the minimum is greater than the configured limit
|
// Module should fail to instantiate because it has too many memories
|
||||||
match Module::new(&engine, r#"(module (memory 4))"#) {
|
match Module::new(&engine, r#"(module (memory 1) (memory 1))"#) {
|
||||||
Ok(_) => panic!("module compilation should fail"),
|
Ok(_) => panic!("module instantiation should fail"),
|
||||||
Err(e) => assert_eq!(
|
Err(e) => assert_eq!(
|
||||||
e.to_string(),
|
e.to_string(),
|
||||||
"memory index 0 has a minimum page size of 4 which exceeds the limit of 3"
|
"defined memories count of 2 exceeds the limit of 1",
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Module should fail to instantiate because the minimum is greater than
|
||||||
|
// the configured limit
|
||||||
|
match Module::new(&engine, r#"(module (memory 4))"#) {
|
||||||
|
Ok(_) => panic!("module instantiation should fail"),
|
||||||
|
Err(e) => assert_eq!(
|
||||||
|
e.to_string(),
|
||||||
|
"memory index 0 has a minimum page size of 4 which exceeds the limit of 3",
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,13 +112,10 @@ fn memory_init() -> Result<()> {
|
|||||||
let mut config = Config::new();
|
let mut config = Config::new();
|
||||||
config.allocation_strategy(InstanceAllocationStrategy::Pooling {
|
config.allocation_strategy(InstanceAllocationStrategy::Pooling {
|
||||||
strategy: PoolingAllocationStrategy::NextAvailable,
|
strategy: PoolingAllocationStrategy::NextAvailable,
|
||||||
module_limits: ModuleLimits {
|
|
||||||
memory_pages: 2,
|
|
||||||
table_elements: 0,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
instance_limits: InstanceLimits {
|
instance_limits: InstanceLimits {
|
||||||
count: 1,
|
count: 1,
|
||||||
|
memory_pages: 2,
|
||||||
|
table_elements: 0,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -137,13 +145,10 @@ fn memory_guard_page_trap() -> Result<()> {
|
|||||||
let mut config = Config::new();
|
let mut config = Config::new();
|
||||||
config.allocation_strategy(InstanceAllocationStrategy::Pooling {
|
config.allocation_strategy(InstanceAllocationStrategy::Pooling {
|
||||||
strategy: PoolingAllocationStrategy::NextAvailable,
|
strategy: PoolingAllocationStrategy::NextAvailable,
|
||||||
module_limits: ModuleLimits {
|
|
||||||
memory_pages: 2,
|
|
||||||
table_elements: 0,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
instance_limits: InstanceLimits {
|
instance_limits: InstanceLimits {
|
||||||
count: 1,
|
count: 1,
|
||||||
|
memory_pages: 2,
|
||||||
|
table_elements: 0,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -196,12 +201,12 @@ fn memory_zeroed() -> Result<()> {
|
|||||||
let mut config = Config::new();
|
let mut config = Config::new();
|
||||||
config.allocation_strategy(InstanceAllocationStrategy::Pooling {
|
config.allocation_strategy(InstanceAllocationStrategy::Pooling {
|
||||||
strategy: PoolingAllocationStrategy::NextAvailable,
|
strategy: PoolingAllocationStrategy::NextAvailable,
|
||||||
module_limits: ModuleLimits {
|
instance_limits: InstanceLimits {
|
||||||
|
count: 1,
|
||||||
memory_pages: 1,
|
memory_pages: 1,
|
||||||
table_elements: 0,
|
table_elements: 0,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
instance_limits: InstanceLimits { count: 1 },
|
|
||||||
});
|
});
|
||||||
config.dynamic_memory_guard_size(0);
|
config.dynamic_memory_guard_size(0);
|
||||||
config.static_memory_guard_size(0);
|
config.static_memory_guard_size(0);
|
||||||
@@ -239,12 +244,12 @@ fn table_limit() -> Result<()> {
|
|||||||
let mut config = Config::new();
|
let mut config = Config::new();
|
||||||
config.allocation_strategy(InstanceAllocationStrategy::Pooling {
|
config.allocation_strategy(InstanceAllocationStrategy::Pooling {
|
||||||
strategy: PoolingAllocationStrategy::NextAvailable,
|
strategy: PoolingAllocationStrategy::NextAvailable,
|
||||||
module_limits: ModuleLimits {
|
instance_limits: InstanceLimits {
|
||||||
|
count: 1,
|
||||||
memory_pages: 1,
|
memory_pages: 1,
|
||||||
table_elements: TABLE_ELEMENTS,
|
table_elements: TABLE_ELEMENTS,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
instance_limits: InstanceLimits { count: 1 },
|
|
||||||
});
|
});
|
||||||
config.dynamic_memory_guard_size(0);
|
config.dynamic_memory_guard_size(0);
|
||||||
config.static_memory_guard_size(0);
|
config.static_memory_guard_size(0);
|
||||||
@@ -252,12 +257,22 @@ fn table_limit() -> Result<()> {
|
|||||||
|
|
||||||
let engine = Engine::new(&config)?;
|
let engine = Engine::new(&config)?;
|
||||||
|
|
||||||
// Module should fail to validate because the minimum is greater than the configured limit
|
// Module should fail to instantiate because it has too many tables
|
||||||
|
match Module::new(&engine, r#"(module (table 1 funcref) (table 1 funcref))"#) {
|
||||||
|
Ok(_) => panic!("module compilation should fail"),
|
||||||
|
Err(e) => assert_eq!(
|
||||||
|
e.to_string(),
|
||||||
|
"defined tables count of 2 exceeds the limit of 1",
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Module should fail to instantiate because the minimum is greater than
|
||||||
|
// the configured limit
|
||||||
match Module::new(&engine, r#"(module (table 31 funcref))"#) {
|
match Module::new(&engine, r#"(module (table 31 funcref))"#) {
|
||||||
Ok(_) => panic!("module compilation should fail"),
|
Ok(_) => panic!("module compilation should fail"),
|
||||||
Err(e) => assert_eq!(
|
Err(e) => assert_eq!(
|
||||||
e.to_string(),
|
e.to_string(),
|
||||||
"table index 0 has a minimum element size of 31 which exceeds the limit of 10"
|
"table index 0 has a minimum element size of 31 which exceeds the limit of 10",
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -316,13 +331,10 @@ fn table_init() -> Result<()> {
|
|||||||
let mut config = Config::new();
|
let mut config = Config::new();
|
||||||
config.allocation_strategy(InstanceAllocationStrategy::Pooling {
|
config.allocation_strategy(InstanceAllocationStrategy::Pooling {
|
||||||
strategy: PoolingAllocationStrategy::NextAvailable,
|
strategy: PoolingAllocationStrategy::NextAvailable,
|
||||||
module_limits: ModuleLimits {
|
|
||||||
memory_pages: 0,
|
|
||||||
table_elements: 6,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
instance_limits: InstanceLimits {
|
instance_limits: InstanceLimits {
|
||||||
count: 1,
|
count: 1,
|
||||||
|
memory_pages: 0,
|
||||||
|
table_elements: 6,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -369,12 +381,12 @@ fn table_zeroed() -> Result<()> {
|
|||||||
let mut config = Config::new();
|
let mut config = Config::new();
|
||||||
config.allocation_strategy(InstanceAllocationStrategy::Pooling {
|
config.allocation_strategy(InstanceAllocationStrategy::Pooling {
|
||||||
strategy: PoolingAllocationStrategy::NextAvailable,
|
strategy: PoolingAllocationStrategy::NextAvailable,
|
||||||
module_limits: ModuleLimits {
|
instance_limits: InstanceLimits {
|
||||||
|
count: 1,
|
||||||
memory_pages: 1,
|
memory_pages: 1,
|
||||||
table_elements: 10,
|
table_elements: 10,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
instance_limits: InstanceLimits { count: 1 },
|
|
||||||
});
|
});
|
||||||
config.dynamic_memory_guard_size(0);
|
config.dynamic_memory_guard_size(0);
|
||||||
config.static_memory_guard_size(0);
|
config.static_memory_guard_size(0);
|
||||||
@@ -413,14 +425,12 @@ fn instantiation_limit() -> Result<()> {
|
|||||||
let mut config = Config::new();
|
let mut config = Config::new();
|
||||||
config.allocation_strategy(InstanceAllocationStrategy::Pooling {
|
config.allocation_strategy(InstanceAllocationStrategy::Pooling {
|
||||||
strategy: PoolingAllocationStrategy::NextAvailable,
|
strategy: PoolingAllocationStrategy::NextAvailable,
|
||||||
module_limits: ModuleLimits {
|
instance_limits: InstanceLimits {
|
||||||
|
count: INSTANCE_LIMIT,
|
||||||
memory_pages: 1,
|
memory_pages: 1,
|
||||||
table_elements: 10,
|
table_elements: 10,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
instance_limits: InstanceLimits {
|
|
||||||
count: INSTANCE_LIMIT,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
config.dynamic_memory_guard_size(0);
|
config.dynamic_memory_guard_size(0);
|
||||||
config.static_memory_guard_size(0);
|
config.static_memory_guard_size(0);
|
||||||
@@ -465,12 +475,12 @@ fn preserve_data_segments() -> Result<()> {
|
|||||||
let mut config = Config::new();
|
let mut config = Config::new();
|
||||||
config.allocation_strategy(InstanceAllocationStrategy::Pooling {
|
config.allocation_strategy(InstanceAllocationStrategy::Pooling {
|
||||||
strategy: PoolingAllocationStrategy::NextAvailable,
|
strategy: PoolingAllocationStrategy::NextAvailable,
|
||||||
module_limits: ModuleLimits {
|
instance_limits: InstanceLimits {
|
||||||
|
count: 2,
|
||||||
memory_pages: 1,
|
memory_pages: 1,
|
||||||
table_elements: 10,
|
table_elements: 10,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
instance_limits: InstanceLimits { count: 2 },
|
|
||||||
});
|
});
|
||||||
let engine = Engine::new(&config)?;
|
let engine = Engine::new(&config)?;
|
||||||
let m = Module::new(
|
let m = Module::new(
|
||||||
@@ -520,13 +530,12 @@ fn multi_memory_with_imported_memories() -> Result<()> {
|
|||||||
let mut config = Config::new();
|
let mut config = Config::new();
|
||||||
config.allocation_strategy(InstanceAllocationStrategy::Pooling {
|
config.allocation_strategy(InstanceAllocationStrategy::Pooling {
|
||||||
strategy: PoolingAllocationStrategy::NextAvailable,
|
strategy: PoolingAllocationStrategy::NextAvailable,
|
||||||
module_limits: ModuleLimits {
|
instance_limits: InstanceLimits {
|
||||||
|
count: 1,
|
||||||
|
memories: 2,
|
||||||
memory_pages: 1,
|
memory_pages: 1,
|
||||||
imported_memories: 1,
|
|
||||||
memories: 1,
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
instance_limits: InstanceLimits { count: 1 },
|
|
||||||
});
|
});
|
||||||
config.wasm_multi_memory(true);
|
config.wasm_multi_memory(true);
|
||||||
|
|
||||||
@@ -567,8 +576,10 @@ fn drop_externref_global_during_module_init() -> Result<()> {
|
|||||||
config.wasm_reference_types(true);
|
config.wasm_reference_types(true);
|
||||||
config.allocation_strategy(InstanceAllocationStrategy::Pooling {
|
config.allocation_strategy(InstanceAllocationStrategy::Pooling {
|
||||||
strategy: PoolingAllocationStrategy::NextAvailable,
|
strategy: PoolingAllocationStrategy::NextAvailable,
|
||||||
module_limits: Default::default(),
|
instance_limits: InstanceLimits {
|
||||||
instance_limits: InstanceLimits { count: 1 },
|
count: 1,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
let engine = Engine::new(&config)?;
|
let engine = Engine::new(&config)?;
|
||||||
@@ -606,3 +617,50 @@ fn drop_externref_global_during_module_init() -> Result<()> {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(target_pointer_width = "64")]
|
||||||
|
fn instance_too_large() -> Result<()> {
|
||||||
|
let mut config = Config::new();
|
||||||
|
config.allocation_strategy(InstanceAllocationStrategy::Pooling {
|
||||||
|
strategy: PoolingAllocationStrategy::NextAvailable,
|
||||||
|
instance_limits: InstanceLimits {
|
||||||
|
size: 16,
|
||||||
|
count: 1,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
let engine = Engine::new(&config)?;
|
||||||
|
let expected = "\
|
||||||
|
instance allocation for this module requires 304 bytes which exceeds the \
|
||||||
|
configured maximum of 16 bytes; breakdown of allocation requirement:
|
||||||
|
|
||||||
|
* 78.95% - 240 bytes - instance state management
|
||||||
|
* 5.26% - 16 bytes - jit store state
|
||||||
|
";
|
||||||
|
match Module::new(&engine, "(module)") {
|
||||||
|
Ok(_) => panic!("should have failed to compile"),
|
||||||
|
Err(e) => assert_eq!(e.to_string(), expected),
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut lots_of_globals = format!("(module");
|
||||||
|
for _ in 0..100 {
|
||||||
|
lots_of_globals.push_str("(global i32 i32.const 0)\n");
|
||||||
|
}
|
||||||
|
lots_of_globals.push_str(")");
|
||||||
|
|
||||||
|
let expected = "\
|
||||||
|
instance allocation for this module requires 1904 bytes which exceeds the \
|
||||||
|
configured maximum of 16 bytes; breakdown of allocation requirement:
|
||||||
|
|
||||||
|
* 12.61% - 240 bytes - instance state management
|
||||||
|
* 84.03% - 1600 bytes - defined globals
|
||||||
|
";
|
||||||
|
match Module::new(&engine, &lots_of_globals) {
|
||||||
|
Ok(_) => panic!("should have failed to compile"),
|
||||||
|
Err(e) => assert_eq!(e.to_string(), expected),
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::sync::{Condvar, Mutex};
|
use std::sync::{Condvar, Mutex};
|
||||||
use wasmtime::{
|
use wasmtime::{
|
||||||
Config, Engine, InstanceAllocationStrategy, InstanceLimits, ModuleLimits,
|
Config, Engine, InstanceAllocationStrategy, InstanceLimits, PoolingAllocationStrategy, Store,
|
||||||
PoolingAllocationStrategy, Store, Strategy,
|
Strategy,
|
||||||
};
|
};
|
||||||
use wasmtime_wast::WastContext;
|
use wasmtime_wast::WastContext;
|
||||||
|
|
||||||
@@ -77,18 +77,11 @@ fn run_wast(wast: &str, strategy: Strategy, pooling: bool) -> anyhow::Result<()>
|
|||||||
// fails to grow, the values here will need to be adjusted.
|
// fails to grow, the values here will need to be adjusted.
|
||||||
cfg.allocation_strategy(InstanceAllocationStrategy::Pooling {
|
cfg.allocation_strategy(InstanceAllocationStrategy::Pooling {
|
||||||
strategy: PoolingAllocationStrategy::NextAvailable,
|
strategy: PoolingAllocationStrategy::NextAvailable,
|
||||||
module_limits: ModuleLimits {
|
|
||||||
imported_memories: 2,
|
|
||||||
imported_tables: 2,
|
|
||||||
imported_globals: 11,
|
|
||||||
memories: 2,
|
|
||||||
tables: 4,
|
|
||||||
globals: 13,
|
|
||||||
memory_pages: 805,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
instance_limits: InstanceLimits {
|
instance_limits: InstanceLimits {
|
||||||
count: 450,
|
count: 450,
|
||||||
|
memories: 2,
|
||||||
|
tables: 4,
|
||||||
|
memory_pages: 805,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user