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
    }
}