From 58aab85680673565339f99cb298ba31a267f3790 Mon Sep 17 00:00:00 2001 From: Peter Huene Date: Wed, 10 Nov 2021 13:21:22 -0800 Subject: [PATCH] Add the `pooling-allocator` feature. This commit adds the `pooling-allocator` feature to both the `wasmtime` and `wasmtime-runtime` crates. The feature controls whether or not the pooling allocator implementation is built into the runtime and exposed as a supported instance allocation strategy in the wasmtime API. The feature is on by default for the `wasmtime` crate. Closes #3513. --- .github/workflows/main.yml | 1 + crates/runtime/Cargo.toml | 5 +- crates/runtime/src/instance/allocator.rs | 2 + crates/runtime/src/lib.rs | 9 +- crates/runtime/src/memory.rs | 1 + crates/runtime/src/table.rs | 1 + crates/wasmtime/Cargo.toml | 7 +- crates/wasmtime/src/config.rs | 283 +---------------------- crates/wasmtime/src/config/pooling.rs | 273 ++++++++++++++++++++++ 9 files changed, 302 insertions(+), 280 deletions(-) create mode 100644 crates/wasmtime/src/config/pooling.rs diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ad42bfa3e313..3f7b2f01c94f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -136,6 +136,7 @@ jobs: - run: cargo check -p wasmtime --no-default-features --features cache - run: cargo check -p wasmtime --no-default-features --features async - run: cargo check -p wasmtime --no-default-features --features uffd + - run: cargo check -p wasmtime --no-default-features --features pooling-allocator - run: cargo check -p wasmtime --no-default-features --features cranelift - run: cargo check -p wasmtime --no-default-features --features cranelift,wat,async,cache diff --git a/crates/runtime/Cargo.toml b/crates/runtime/Cargo.toml index 3d2016b09f47..c883a161d6d9 100644 --- a/crates/runtime/Cargo.toml +++ b/crates/runtime/Cargo.toml @@ -49,8 +49,11 @@ default = [] async = ["wasmtime-fiber"] +# Enables support for the pooling instance allocator +pooling-allocator = [] + # Enables support for userfaultfd in the pooling allocator when building on Linux -uffd = ["userfaultfd"] +uffd = ["userfaultfd", "pooling-allocator"] # Enables trap handling using POSIX signals instead of Mach exceptions on MacOS. # It is useful for applications that do not bind their own exception ports and diff --git a/crates/runtime/src/instance/allocator.rs b/crates/runtime/src/instance/allocator.rs index abcb94b11f48..55ffd3181822 100644 --- a/crates/runtime/src/instance/allocator.rs +++ b/crates/runtime/src/instance/allocator.rs @@ -23,8 +23,10 @@ use wasmtime_environ::{ SignatureIndex, TableInitializer, TrapCode, VMOffsets, WasmType, WASM_PAGE_SIZE, }; +#[cfg(feature = "pooling-allocator")] mod pooling; +#[cfg(feature = "pooling-allocator")] pub use self::pooling::{ InstanceLimits, ModuleLimits, PoolingAllocationStrategy, PoolingInstanceAllocator, }; diff --git a/crates/runtime/src/lib.rs b/crates/runtime/src/lib.rs index 6fc8e66c3d88..ba8b544b3a8f 100644 --- a/crates/runtime/src/lib.rs +++ b/crates/runtime/src/lib.rs @@ -40,9 +40,12 @@ pub use crate::export::*; pub use crate::externref::*; pub use crate::imports::Imports; pub use crate::instance::{ - InstanceAllocationRequest, InstanceAllocator, InstanceHandle, InstanceLimits, - InstantiationError, LinkError, ModuleLimits, OnDemandInstanceAllocator, - PoolingAllocationStrategy, PoolingInstanceAllocator, StorePtr, + InstanceAllocationRequest, InstanceAllocator, InstanceHandle, InstantiationError, LinkError, + OnDemandInstanceAllocator, StorePtr, +}; +#[cfg(feature = "pooling-allocator")] +pub use crate::instance::{ + InstanceLimits, ModuleLimits, PoolingAllocationStrategy, PoolingInstanceAllocator, }; pub use crate::jit_int::GdbJitImageRegistration; pub use crate::memory::{Memory, RuntimeLinearMemory, RuntimeMemoryCreator}; diff --git a/crates/runtime/src/memory.rs b/crates/runtime/src/memory.rs index 6ab2ed83fc9c..07c0c619cc53 100644 --- a/crates/runtime/src/memory.rs +++ b/crates/runtime/src/memory.rs @@ -364,6 +364,7 @@ impl Memory { } /// Returns whether or not the underlying storage of the memory is "static". + #[cfg(feature = "pooling-allocator")] pub(crate) fn is_static(&self) -> bool { if let Memory::Static { .. } = self { true diff --git a/crates/runtime/src/table.rs b/crates/runtime/src/table.rs index 53a1f8b7b36d..5aed2333a6a4 100644 --- a/crates/runtime/src/table.rs +++ b/crates/runtime/src/table.rs @@ -186,6 +186,7 @@ impl Table { } /// Returns whether or not the underlying storage of the table is "static". + #[cfg(feature = "pooling-allocator")] pub(crate) fn is_static(&self) -> bool { if let Table::Static { .. } = self { true diff --git a/crates/wasmtime/Cargo.toml b/crates/wasmtime/Cargo.toml index 7c96eea19372..12c776cd6571 100644 --- a/crates/wasmtime/Cargo.toml +++ b/crates/wasmtime/Cargo.toml @@ -52,7 +52,7 @@ wasi-cap-std-sync = { path = "../wasi-common/cap-std-sync" } maintenance = { status = "actively-developed" } [features] -default = ['async', 'cache', 'wat', 'jitdump', 'parallel-compilation', 'cranelift'] +default = ['async', 'cache', 'wat', 'jitdump', 'parallel-compilation', 'cranelift', 'pooling-allocator'] # An on-by-default feature enabling runtime compilation of WebAssembly modules # with the Cranelift compiler. Cranelift is the default compilation backend of @@ -76,8 +76,11 @@ cache = ["wasmtime-cache"] # `async fn` and calling functions asynchronously. async = ["wasmtime-fiber", "wasmtime-runtime/async", "async-trait"] +# Enables support for the pooling instance allocation strategy +pooling-allocator = ["wasmtime-runtime/pooling-allocator"] + # Enables userfaultfd support in the runtime's pooling allocator when building on Linux -uffd = ["wasmtime-runtime/uffd"] +uffd = ["wasmtime-runtime/uffd", "pooling-allocator"] # Enables support for all architectures in Cranelift, allowing # cross-compilation using the `wasmtime` crate's API, notably the diff --git a/crates/wasmtime/src/config.rs b/crates/wasmtime/src/config.rs index be7dd93d9be3..00a04dd19d6b 100644 --- a/crates/wasmtime/src/config.rs +++ b/crates/wasmtime/src/config.rs @@ -12,281 +12,13 @@ use wasmparser::WasmFeatures; use wasmtime_cache::CacheConfig; use wasmtime_environ::{CompilerBuilder, Tunables}; use wasmtime_jit::{JitDumpAgent, NullProfilerAgent, ProfilingAgent, VTuneAgent}; -use wasmtime_runtime::{ - InstanceAllocator, OnDemandInstanceAllocator, PoolingInstanceAllocator, RuntimeMemoryCreator, -}; +use wasmtime_runtime::{InstanceAllocator, OnDemandInstanceAllocator, RuntimeMemoryCreator}; -/// 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`](Config::static_memory_maximum_size) setting and this value cannot - /// exceed the configured static memory maximum size. - pub memory_pages: u64, -} +#[cfg(feature = "pooling-allocator")] +mod pooling; -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 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 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, -} - -impl Default for PoolingAllocationStrategy { - fn default() -> Self { - match wasmtime_runtime::PoolingAllocationStrategy::default() { - wasmtime_runtime::PoolingAllocationStrategy::NextAvailable => Self::NextAvailable, - wasmtime_runtime::PoolingAllocationStrategy::Random => Self::Random, - } - } -} - -// 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 for PoolingAllocationStrategy { - fn into(self) -> wasmtime_runtime::PoolingAllocationStrategy { - match self { - Self::NextAvailable => wasmtime_runtime::PoolingAllocationStrategy::NextAvailable, - Self::Random => wasmtime_runtime::PoolingAllocationStrategy::Random, - } - } -} +#[cfg(feature = "pooling-allocator")] +pub use self::pooling::*; /// Represents the module instance allocation strategy to use. #[derive(Clone)] @@ -303,6 +35,7 @@ pub enum InstanceAllocationStrategy { /// A pool of resources is created in advance and module instantiation reuses resources /// from the pool. Resources are returned to the pool when the `Store` referencing the instance /// is dropped. + #[cfg(feature = "pooling-allocator")] Pooling { /// The allocation strategy to use. strategy: PoolingAllocationStrategy, @@ -315,6 +48,7 @@ pub enum InstanceAllocationStrategy { impl InstanceAllocationStrategy { /// The default pooling instance allocation strategy. + #[cfg(feature = "pooling-allocator")] pub fn pooling() -> Self { Self::Pooling { strategy: PoolingAllocationStrategy::default(), @@ -1330,11 +1064,12 @@ impl Config { self.mem_creator.clone(), stack_size, ))), + #[cfg(feature = "pooling-allocator")] InstanceAllocationStrategy::Pooling { strategy, module_limits, instance_limits, - } => Ok(Box::new(PoolingInstanceAllocator::new( + } => Ok(Box::new(wasmtime_runtime::PoolingInstanceAllocator::new( strategy.into(), module_limits.into(), instance_limits.into(), diff --git a/crates/wasmtime/src/config/pooling.rs b/crates/wasmtime/src/config/pooling.rs new file mode 100644 index 000000000000..a2cbc470017e --- /dev/null +++ b/crates/wasmtime/src/config/pooling.rs @@ -0,0 +1,273 @@ +//! 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 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 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, +} + +impl Default for PoolingAllocationStrategy { + fn default() -> Self { + match wasmtime_runtime::PoolingAllocationStrategy::default() { + wasmtime_runtime::PoolingAllocationStrategy::NextAvailable => Self::NextAvailable, + wasmtime_runtime::PoolingAllocationStrategy::Random => Self::Random, + } + } +} + +// 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 for PoolingAllocationStrategy { + fn into(self) -> wasmtime_runtime::PoolingAllocationStrategy { + match self { + Self::NextAvailable => wasmtime_runtime::PoolingAllocationStrategy::NextAvailable, + Self::Random => wasmtime_runtime::PoolingAllocationStrategy::Random, + } + } +}