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> {
16    inner: wasmtime::Store<T>,
17    epoch_tick_interval: Duration,
18}
19
20impl<T> 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
53impl<T> AsRef<wasmtime::Store<T>> for Store<T> {
54    fn as_ref(&self) -> &wasmtime::Store<T> {
55        &self.inner
56    }
57}
58
59impl<T> AsMut<wasmtime::Store<T>> for Store<T> {
60    fn as_mut(&mut self) -> &mut wasmtime::Store<T> {
61        &mut self.inner
62    }
63}
64
65impl<T> wasmtime::AsContext for Store<T> {
66    type Data = T;
67
68    fn as_context(&self) -> wasmtime::StoreContext<'_, Self::Data> {
69        self.inner.as_context()
70    }
71}
72
73impl<T> wasmtime::AsContextMut for Store<T> {
74    fn as_context_mut(&mut self) -> wasmtime::StoreContextMut<'_, Self::Data> {
75        self.inner.as_context_mut()
76    }
77}
78
79/// A builder interface for configuring a new [`Store`].
80///
81/// A new [`StoreBuilder`] can be obtained with [`crate::Engine::store_builder`].
82pub struct StoreBuilder {
83    engine: WasmtimeEngine,
84    epoch_tick_interval: Duration,
85    store_limits: StoreLimitsAsync,
86}
87
88impl StoreBuilder {
89    // Called by Engine::store_builder.
90    pub(crate) fn new(engine: WasmtimeEngine, epoch_tick_interval: Duration) -> Self {
91        Self {
92            engine,
93            epoch_tick_interval,
94            store_limits: StoreLimitsAsync::default(),
95        }
96    }
97
98    /// Sets a maximum memory allocation limit.
99    ///
100    /// See [`wasmtime::ResourceLimiter::memory_growing`] (`maximum`) for
101    /// details on how this limit is enforced.
102    pub fn max_memory_size(&mut self, max_memory_size: usize) {
103        self.store_limits = StoreLimitsAsync::new(Some(max_memory_size), None);
104    }
105
106    /// Builds a [`Store`] from this builder with given host state data.
107    ///
108    /// The `T` parameter must provide access to a [`State`] via `impl
109    /// AsMut<State>`.
110    pub fn build<T: AsState>(self, mut data: T) -> Result<Store<T>> {
111        data.as_state().store_limits = self.store_limits;
112
113        let mut inner = wasmtime::Store::new(&self.engine, data);
114        inner.limiter_async(|data| &mut data.as_state().store_limits);
115
116        // With epoch interruption enabled, there must be _some_ deadline set
117        // or execution will trap immediately. Since this is a delta, we need
118        // to avoid overflow so we'll use 2^63 which is still "practically
119        // forever" for any plausible tick interval.
120        inner.set_epoch_deadline(u64::MAX / 2);
121
122        Ok(Store {
123            inner,
124            epoch_tick_interval: self.epoch_tick_interval,
125        })
126    }
127}
128
129/// For consumers that need to use a type other than [`State`] as the [`Store`]
130/// `data`, this trait must be implemented for that type.
131pub trait AsState {
132    /// Gives access to the inner [`State`].
133    fn as_state(&mut self) -> &mut State;
134}
135
136impl AsState for State {
137    fn as_state(&mut self) -> &mut State {
138        self
139    }
140}