spin_core/
store.rs

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