spin_core/store.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
use anyhow::Result;
use std::time::{Duration, Instant};
use crate::{limits::StoreLimitsAsync, State, WasmtimeEngine};
#[cfg(doc)]
use crate::EngineBuilder;
/// A `Store` holds the runtime state of a Spin instance.
///
/// In general, a `Store` is expected to live only for the lifetime of a single
/// Spin trigger invocation.
///
/// A `Store` can be built with a [`StoreBuilder`].
pub struct Store<T> {
inner: wasmtime::Store<T>,
epoch_tick_interval: Duration,
}
impl<T> Store<T> {
/// Sets the execution deadline.
///
/// This is a rough deadline; an instance will trap some time after this
/// deadline, determined by [`EngineBuilder::epoch_tick_interval`] and
/// details of the system's thread scheduler.
///
/// See [`wasmtime::Store::set_epoch_deadline`](https://docs.rs/wasmtime/latest/wasmtime/struct.Store.html#method.set_epoch_deadline).
pub fn set_deadline(&mut self, deadline: Instant) {
let now = Instant::now();
let duration = deadline - now;
let ticks = if duration.is_zero() {
tracing::warn!("Execution deadline set in past: {deadline:?} < {now:?}");
0
} else {
let ticks = duration.as_micros() / self.epoch_tick_interval.as_micros();
let ticks = ticks.min(u64::MAX as u128) as u64;
ticks + 1 // Add one to allow for current partially-completed tick
};
self.inner.set_epoch_deadline(ticks);
}
/// Provides access to the inner [`wasmtime::Store`]'s data.
pub fn data(&self) -> &T {
self.inner.data()
}
/// Provides access to the inner [`wasmtime::Store`]'s data.
pub fn data_mut(&mut self) -> &mut T {
self.inner.data_mut()
}
}
impl<T> AsRef<wasmtime::Store<T>> for Store<T> {
fn as_ref(&self) -> &wasmtime::Store<T> {
&self.inner
}
}
impl<T> AsMut<wasmtime::Store<T>> for Store<T> {
fn as_mut(&mut self) -> &mut wasmtime::Store<T> {
&mut self.inner
}
}
impl<T> wasmtime::AsContext for Store<T> {
type Data = T;
fn as_context(&self) -> wasmtime::StoreContext<'_, Self::Data> {
self.inner.as_context()
}
}
impl<T> wasmtime::AsContextMut for Store<T> {
fn as_context_mut(&mut self) -> wasmtime::StoreContextMut<'_, Self::Data> {
self.inner.as_context_mut()
}
}
/// A builder interface for configuring a new [`Store`].
///
/// A new [`StoreBuilder`] can be obtained with [`crate::Engine::store_builder`].
pub struct StoreBuilder {
engine: WasmtimeEngine,
epoch_tick_interval: Duration,
store_limits: StoreLimitsAsync,
}
impl StoreBuilder {
// Called by Engine::store_builder.
pub(crate) fn new(engine: WasmtimeEngine, epoch_tick_interval: Duration) -> Self {
Self {
engine,
epoch_tick_interval,
store_limits: StoreLimitsAsync::default(),
}
}
/// Sets a maximum memory allocation limit.
///
/// See [`wasmtime::ResourceLimiter::memory_growing`] (`maximum`) for
/// details on how this limit is enforced.
pub fn max_memory_size(&mut self, max_memory_size: usize) {
self.store_limits = StoreLimitsAsync::new(Some(max_memory_size), None);
}
/// Builds a [`Store`] from this builder with given host state data.
///
/// The `T` parameter must provide access to a [`State`] via `impl
/// AsMut<State>`.
pub fn build<T: AsState>(self, mut data: T) -> Result<Store<T>> {
data.as_state().store_limits = self.store_limits;
let mut inner = wasmtime::Store::new(&self.engine, data);
inner.limiter_async(|data| &mut data.as_state().store_limits);
// With epoch interruption enabled, there must be _some_ deadline set
// or execution will trap immediately. Since this is a delta, we need
// to avoid overflow so we'll use 2^63 which is still "practically
// forever" for any plausible tick interval.
inner.set_epoch_deadline(u64::MAX / 2);
Ok(Store {
inner,
epoch_tick_interval: self.epoch_tick_interval,
})
}
}
/// For consumers that need to use a type other than [`State`] as the [`Store`]
/// `data`, this trait must be implemented for that type.
pub trait AsState {
/// Gives access to the inner [`State`].
fn as_state(&mut self) -> &mut State;
}
impl AsState for State {
fn as_state(&mut self) -> &mut State {
self
}
}