Improve documentation around ResourceLimiter (#4173)
* Improve documentation around `ResourceLimiter` This commit takes a pass through the `Store::limiter` method and related types/traits to improve the documentation with an example and soup up any recent developments in the documentation. Closes #4138 * Fix a broken doc link
This commit is contained in:
@@ -7,8 +7,26 @@ pub const DEFAULT_MEMORY_LIMIT: usize = 10000;
|
|||||||
|
|
||||||
/// Used by hosts to limit resource consumption of instances.
|
/// Used by hosts to limit resource consumption of instances.
|
||||||
///
|
///
|
||||||
/// An instance can be created with a resource limiter so that hosts can take into account
|
/// This trait is used in conjunction with the
|
||||||
/// non-WebAssembly resource usage to determine if a linear memory or table should grow.
|
/// [`Store::limiter`](crate::Store::limiter) to synchronously limit the
|
||||||
|
/// allocation of resources within a store. As a store-level limit this means
|
||||||
|
/// that all creation of instances, memories, and tables are limited within the
|
||||||
|
/// store. Resources limited via this trait are primarily related to memory and
|
||||||
|
/// limiting CPU resources needs to be done with something such as
|
||||||
|
/// [`Config::consume_fuel`](crate::Config::consume_fuel) or
|
||||||
|
/// [`Config::epoch_interruption`](crate::Config::epoch_interruption).
|
||||||
|
///
|
||||||
|
/// Note that this trait does not limit 100% of memory allocated via a
|
||||||
|
/// [`Store`](crate::Store). Wasmtime will still allocate memory to track data
|
||||||
|
/// structures and additionally embedder-specific memory allocations are not
|
||||||
|
/// tracked via this trait. This trait only limits resources allocated by a
|
||||||
|
/// WebAssembly instance itself.
|
||||||
|
///
|
||||||
|
/// This trait is intended for synchronously limiting the resources of a module.
|
||||||
|
/// If your use case requires blocking to answer whether a request is permitted
|
||||||
|
/// or not and you're otherwise working in an asynchronous context the
|
||||||
|
/// [`ResourceLimiterAsync`] trait is also provided to avoid blocking an OS
|
||||||
|
/// thread while a limit is determined.
|
||||||
pub trait ResourceLimiter {
|
pub trait ResourceLimiter {
|
||||||
/// Notifies the resource limiter that an instance's linear memory has been
|
/// Notifies the resource limiter that an instance's linear memory has been
|
||||||
/// requested to grow.
|
/// requested to grow.
|
||||||
@@ -19,6 +37,9 @@ pub trait ResourceLimiter {
|
|||||||
/// instance allocator, also in bytes. A value of `None`
|
/// instance allocator, also in bytes. A value of `None`
|
||||||
/// indicates that the linear memory is unbounded.
|
/// indicates that the linear memory is unbounded.
|
||||||
///
|
///
|
||||||
|
/// The `current` and `desired` amounts are guaranteed to always be
|
||||||
|
/// multiples of the WebAssembly page size, 64KiB.
|
||||||
|
///
|
||||||
/// This function should return `true` to indicate that the growing
|
/// This function should return `true` to indicate that the growing
|
||||||
/// operation is permitted or `false` if not permitted. Returning `true`
|
/// operation is permitted or `false` if not permitted. Returning `true`
|
||||||
/// when a maximum has been exceeded will have no effect as the linear
|
/// when a maximum has been exceeded will have no effect as the linear
|
||||||
@@ -28,6 +49,12 @@ pub trait ResourceLimiter {
|
|||||||
/// `memory.grow`. Requests where the allocation requested size doesn't fit
|
/// `memory.grow`. Requests where the allocation requested size doesn't fit
|
||||||
/// in `usize` or exceeds the memory's listed maximum size may not invoke
|
/// in `usize` or exceeds the memory's listed maximum size may not invoke
|
||||||
/// this method.
|
/// this method.
|
||||||
|
///
|
||||||
|
/// Returning `false` from this method will cause the `memory.grow`
|
||||||
|
/// instruction in a module to return -1 (failure), or in the case of an
|
||||||
|
/// embedder API calling [`Memory::new`](crate::Memory::new) or
|
||||||
|
/// [`Memory::grow`](crate::Memory::grow) an error will be returned from
|
||||||
|
/// those methods.
|
||||||
fn memory_growing(&mut self, current: usize, desired: usize, maximum: Option<usize>) -> bool;
|
fn memory_growing(&mut self, current: usize, desired: usize, maximum: Option<usize>) -> bool;
|
||||||
|
|
||||||
/// Notifies the resource limiter that growing a linear memory, permitted by
|
/// Notifies the resource limiter that growing a linear memory, permitted by
|
||||||
@@ -38,16 +65,24 @@ pub trait ResourceLimiter {
|
|||||||
/// memory. In that case, `error` might be downcastable to a `std::io::Error`.
|
/// memory. In that case, `error` might be downcastable to a `std::io::Error`.
|
||||||
fn memory_grow_failed(&mut self, _error: &anyhow::Error) {}
|
fn memory_grow_failed(&mut self, _error: &anyhow::Error) {}
|
||||||
|
|
||||||
/// Notifies the resource limiter that an instance's table has been requested to grow.
|
/// Notifies the resource limiter that an instance's table has been
|
||||||
|
/// requested to grow.
|
||||||
///
|
///
|
||||||
/// * `current` is the current number of elements in the table.
|
/// * `current` is the current number of elements in the table.
|
||||||
/// * `desired` is the desired number of elements in the table.
|
/// * `desired` is the desired number of elements in the table.
|
||||||
/// * `maximum` is either the table's maximum or a maximum from an instance allocator.
|
/// * `maximum` is either the table's maximum or a maximum from an instance
|
||||||
/// A value of `None` indicates that the table is unbounded.
|
/// allocator. A value of `None` indicates that the table is unbounded.
|
||||||
///
|
///
|
||||||
/// This function should return `true` to indicate that the growing operation is permitted or
|
/// This function should return `true` to indicate that the growing
|
||||||
/// `false` if not permitted. Returning `true` when a maximum has been exceeded will have no
|
/// operation is permitted or `false` if not permitted. Returning `true`
|
||||||
/// effect as the table will not grow.
|
/// when a maximum has been exceeded will have no effect as the table will
|
||||||
|
/// not grow.
|
||||||
|
///
|
||||||
|
/// Currently in Wasmtime each table element requires a pointer's worth of
|
||||||
|
/// space (e.g. `mem::size_of::<usize>()`).
|
||||||
|
///
|
||||||
|
/// Like `memory_growing` returning `false` from this function will cause
|
||||||
|
/// `table.grow` to return -1 or embedder APIs will return an error.
|
||||||
fn table_growing(&mut self, current: u32, desired: u32, maximum: Option<u32>) -> bool;
|
fn table_growing(&mut self, current: u32, desired: u32, maximum: Option<u32>) -> bool;
|
||||||
|
|
||||||
/// Notifies the resource limiter that growing a linear memory, permitted by
|
/// Notifies the resource limiter that growing a linear memory, permitted by
|
||||||
@@ -68,7 +103,7 @@ pub trait ResourceLimiter {
|
|||||||
|
|
||||||
/// The maximum number of tables that can be created for a `Store`.
|
/// The maximum number of tables that can be created for a `Store`.
|
||||||
///
|
///
|
||||||
/// Module instantiation will fail if this limit is exceeded.
|
/// Creation of tables will fail if this limit is exceeded.
|
||||||
///
|
///
|
||||||
/// This value defaults to 10,000.
|
/// This value defaults to 10,000.
|
||||||
fn tables(&self) -> usize {
|
fn tables(&self) -> usize {
|
||||||
@@ -77,7 +112,7 @@ pub trait ResourceLimiter {
|
|||||||
|
|
||||||
/// The maximum number of linear memories that can be created for a `Store`
|
/// The maximum number of linear memories that can be created for a `Store`
|
||||||
///
|
///
|
||||||
/// Instantiation will fail with an error if this limit is exceeded.
|
/// Creation of memories will fail with an error if this limit is exceeded.
|
||||||
///
|
///
|
||||||
/// This value defaults to 10,000.
|
/// This value defaults to 10,000.
|
||||||
fn memories(&self) -> usize {
|
fn memories(&self) -> usize {
|
||||||
@@ -85,15 +120,24 @@ pub trait ResourceLimiter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "async")]
|
/// Used by hosts to limit resource consumption of instances, blocking
|
||||||
/// Used by hosts to limit resource consumption of instances. Identical to
|
/// asynchronously if necessary.
|
||||||
/// [`ResourceLimiter`], except that the `memory_growing` and `table_growing`
|
///
|
||||||
/// functions are async. Must be used with an async [`Store`](`crate::Store`).
|
/// This trait is identical to [`ResourceLimiter`], except that the
|
||||||
|
/// `memory_growing` and `table_growing` functions are `async`. Must be used
|
||||||
|
/// with an async [`Store`](`crate::Store`) configured via
|
||||||
|
/// [`Config::async_support`](crate::Config::async_support).
|
||||||
///
|
///
|
||||||
/// This trait is used with
|
/// This trait is used with
|
||||||
/// [`Store::limiter_async`](`crate::Store::limiter_async`)`: see those docs
|
/// [`Store::limiter_async`](`crate::Store::limiter_async`)`: see those docs
|
||||||
/// for restrictions on using other Wasmtime interfaces with an async resource
|
/// for restrictions on using other Wasmtime interfaces with an async resource
|
||||||
/// limiter.
|
/// limiter. Additionally see [`ResourceLimiter`] for more information about
|
||||||
|
/// limiting resources from WebAssembly.
|
||||||
|
///
|
||||||
|
/// The `async` here enables embedders that are already using asynchronous
|
||||||
|
/// execution of WebAssembly to block the WebAssembly, but no the OS thread, to
|
||||||
|
/// answer the question whether growing a memory or table is allowed.
|
||||||
|
#[cfg(feature = "async")]
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
pub trait ResourceLimiterAsync {
|
pub trait ResourceLimiterAsync {
|
||||||
/// Async version of [`ResourceLimiter::memory_growing`]
|
/// Async version of [`ResourceLimiter::memory_growing`]
|
||||||
@@ -134,6 +178,9 @@ pub struct StoreLimitsBuilder(StoreLimits);
|
|||||||
|
|
||||||
impl StoreLimitsBuilder {
|
impl StoreLimitsBuilder {
|
||||||
/// Creates a new [`StoreLimitsBuilder`].
|
/// Creates a new [`StoreLimitsBuilder`].
|
||||||
|
///
|
||||||
|
/// See the documentation on each builder method for the default for each
|
||||||
|
/// value.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self(StoreLimits::default())
|
Self(StoreLimits::default())
|
||||||
}
|
}
|
||||||
@@ -195,6 +242,13 @@ impl StoreLimitsBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Provides limits for a [`Store`](crate::Store).
|
/// Provides limits for a [`Store`](crate::Store).
|
||||||
|
///
|
||||||
|
/// This type is created with a [`StoreLimitsBuilder`] and is typically used in
|
||||||
|
/// conjuction with [`Store::limiter`](crate::Store::limiter).
|
||||||
|
///
|
||||||
|
/// This is a convenience type included to avoid needing to implement the
|
||||||
|
/// [`ResourceLimiter`] trait if your use case fits in the static configuration
|
||||||
|
/// that this [`StoreLimits`] provides.
|
||||||
pub struct StoreLimits {
|
pub struct StoreLimits {
|
||||||
memory_size: Option<usize>,
|
memory_size: Option<usize>,
|
||||||
table_elements: Option<u32>,
|
table_elements: Option<u32>,
|
||||||
@@ -215,7 +269,6 @@ impl Default for StoreLimits {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "async", async_trait::async_trait)]
|
|
||||||
impl ResourceLimiter for StoreLimits {
|
impl ResourceLimiter for StoreLimits {
|
||||||
fn memory_growing(&mut self, _current: usize, desired: usize, _maximum: Option<usize>) -> bool {
|
fn memory_growing(&mut self, _current: usize, desired: usize, _maximum: Option<usize>) -> bool {
|
||||||
match self.memory_size {
|
match self.memory_size {
|
||||||
|
|||||||
@@ -567,12 +567,57 @@ impl<T> Store<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Configures the [`ResourceLimiter`](crate::ResourceLimiter) used to limit
|
/// Configures the [`ResourceLimiter`] used to limit resource creation
|
||||||
/// resource creation within this [`Store`].
|
/// within this [`Store`].
|
||||||
|
///
|
||||||
|
/// Whenever resources such as linear memory, tables, or instances are
|
||||||
|
/// allocated the `limiter` specified here is invoked with the store's data
|
||||||
|
/// `T` and the returned [`ResourceLimiter`] is used to limit the operation
|
||||||
|
/// being allocated. The returned [`ResourceLimiter`] is intended to live
|
||||||
|
/// within the `T` itself, for example by storing a
|
||||||
|
/// [`StoreLimits`](crate::StoreLimits).
|
||||||
///
|
///
|
||||||
/// Note that this limiter is only used to limit the creation/growth of
|
/// Note that this limiter is only used to limit the creation/growth of
|
||||||
/// resources in the future, this does not retroactively attempt to apply
|
/// resources in the future, this does not retroactively attempt to apply
|
||||||
/// limits to the [`Store`].
|
/// limits to the [`Store`].
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use wasmtime::*;
|
||||||
|
///
|
||||||
|
/// struct MyApplicationState {
|
||||||
|
/// my_state: u32,
|
||||||
|
/// limits: StoreLimits,
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// let engine = Engine::default();
|
||||||
|
/// let my_state = MyApplicationState {
|
||||||
|
/// my_state: 42,
|
||||||
|
/// limits: StoreLimitsBuilder::new()
|
||||||
|
/// .memory_size(1 << 20 /* 1 MB */)
|
||||||
|
/// .instances(2)
|
||||||
|
/// .build(),
|
||||||
|
/// };
|
||||||
|
/// let mut store = Store::new(&engine, my_state);
|
||||||
|
/// store.limiter(|state| &mut state.limits);
|
||||||
|
///
|
||||||
|
/// // Creation of smaller memories is allowed
|
||||||
|
/// Memory::new(&mut store, MemoryType::new(1, None)).unwrap();
|
||||||
|
///
|
||||||
|
/// // Creation of a larger memory, however, will exceed the 1MB limit we've
|
||||||
|
/// // configured
|
||||||
|
/// assert!(Memory::new(&mut store, MemoryType::new(1000, None)).is_err());
|
||||||
|
///
|
||||||
|
/// // The number of instances in this store is limited to 2, so the third
|
||||||
|
/// // instance here should fail.
|
||||||
|
/// let module = Module::new(&engine, "(module)").unwrap();
|
||||||
|
/// assert!(Instance::new(&mut store, &module, &[]).is_ok());
|
||||||
|
/// assert!(Instance::new(&mut store, &module, &[]).is_ok());
|
||||||
|
/// assert!(Instance::new(&mut store, &module, &[]).is_err());
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// [`ResourceLimiter`]: crate::ResourceLimiter
|
||||||
pub fn limiter(
|
pub fn limiter(
|
||||||
&mut self,
|
&mut self,
|
||||||
mut limiter: impl FnMut(&mut T) -> &mut (dyn crate::ResourceLimiter) + Send + Sync + 'static,
|
mut limiter: impl FnMut(&mut T) -> &mut (dyn crate::ResourceLimiter) + Send + Sync + 'static,
|
||||||
@@ -592,20 +637,12 @@ impl<T> Store<T> {
|
|||||||
inner.limiter = Some(ResourceLimiterInner::Sync(Box::new(limiter)));
|
inner.limiter = Some(ResourceLimiterInner::Sync(Box::new(limiter)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(nightlydoc, doc(cfg(feature = "async")))]
|
|
||||||
/// Configures the [`ResourceLimiterAsync`](crate::ResourceLimiterAsync)
|
/// Configures the [`ResourceLimiterAsync`](crate::ResourceLimiterAsync)
|
||||||
/// used to limit resource creation within this [`Store`]. Must be used
|
/// used to limit resource creation within this [`Store`].
|
||||||
/// with an async `Store`!.
|
|
||||||
///
|
///
|
||||||
/// Note that this limiter is only used to limit the creation/growth of
|
/// This method is an asynchronous variant of the [`Store::limiter`] method
|
||||||
/// resources in the future, this does not retroactively attempt to apply
|
/// where the embedder can block the wasm request for more resources with
|
||||||
/// limits to the [`Store`].
|
/// host `async` execution of futures.
|
||||||
///
|
|
||||||
/// This variation on the [`ResourceLimiter`](`crate::ResourceLimiter`)
|
|
||||||
/// makes the `memory_growing` and `table_growing` functions `async`. This
|
|
||||||
/// means that, as part of your resource limiting strategy, the async
|
|
||||||
/// resource limiter may yield execution until a resource becomes
|
|
||||||
/// available.
|
|
||||||
///
|
///
|
||||||
/// By using a [`ResourceLimiterAsync`](`crate::ResourceLimiterAsync`)
|
/// By using a [`ResourceLimiterAsync`](`crate::ResourceLimiterAsync`)
|
||||||
/// with a [`Store`], you can no longer use
|
/// with a [`Store`], you can no longer use
|
||||||
@@ -617,7 +654,14 @@ impl<T> Store<T> {
|
|||||||
/// [`Memory::grow_async`](`crate::Memory::grow_async`),
|
/// [`Memory::grow_async`](`crate::Memory::grow_async`),
|
||||||
/// [`Table::new_async`](`crate::Table::new_async`), and
|
/// [`Table::new_async`](`crate::Table::new_async`), and
|
||||||
/// [`Table::grow_async`](`crate::Table::grow_async`).
|
/// [`Table::grow_async`](`crate::Table::grow_async`).
|
||||||
|
///
|
||||||
|
/// Note that this limiter is only used to limit the creation/growth of
|
||||||
|
/// resources in the future, this does not retroactively attempt to apply
|
||||||
|
/// limits to the [`Store`]. Additionally this must be used with an async
|
||||||
|
/// [`Store`] configured via
|
||||||
|
/// [`Config::async_support`](crate::Config::async_support).
|
||||||
#[cfg(feature = "async")]
|
#[cfg(feature = "async")]
|
||||||
|
#[cfg_attr(nightlydoc, doc(cfg(feature = "async")))]
|
||||||
pub fn limiter_async(
|
pub fn limiter_async(
|
||||||
&mut self,
|
&mut self,
|
||||||
mut limiter: impl FnMut(&mut T) -> &mut (dyn crate::ResourceLimiterAsync)
|
mut limiter: impl FnMut(&mut T) -> &mut (dyn crate::ResourceLimiterAsync)
|
||||||
|
|||||||
Reference in New Issue
Block a user