wasm/execution/
resumable.rs

1use core::num::NonZeroU64;
2
3use alloc::vec::Vec;
4
5use crate::{
6    addrs::FuncAddr,
7    core::slotmap::{SlotMap, SlotMapKey},
8    value_stack::Stack,
9    Value,
10};
11
12#[derive(Debug)]
13pub(crate) struct Resumable {
14    pub(crate) stack: Stack,
15    pub(crate) pc: usize,
16    pub(crate) stp: usize,
17    pub(crate) current_func_addr: FuncAddr,
18    pub(crate) maybe_fuel: Option<u64>,
19}
20
21#[derive(Default)]
22pub(crate) struct Dormitory(SlotMap<Resumable>);
23
24impl Dormitory {
25    pub(crate) fn insert(&mut self, resumable: Resumable) -> InvokedResumableRef {
26        let key = self.0.insert(resumable);
27        InvokedResumableRef { key }
28    }
29
30    pub(crate) fn get_mut(
31        &mut self,
32        resumable_ref: &InvokedResumableRef,
33    ) -> Option<&mut Resumable> {
34        self.0.get_mut(&resumable_ref.key)
35    }
36
37    pub(crate) fn remove(&mut self, resumable_ref: InvokedResumableRef) -> Option<Resumable> {
38        self.0.remove(&resumable_ref.key)
39    }
40}
41
42/// # Note
43///
44/// Dropping a resumable ref does not deallocate the resumable anymore. It is up
45/// to the user to implement such garbage collection algorithms by using
46/// [`Store::drop_resumable`](crate::Store::drop_resumable) to prevent unnecessary memory usage.
47pub struct InvokedResumableRef {
48    pub(crate) key: SlotMapKey<Resumable>,
49}
50
51pub struct FreshResumableRef {
52    pub(crate) func_addr: FuncAddr,
53    pub(crate) params: Vec<Value>,
54    pub(crate) maybe_fuel: Option<u64>,
55}
56
57/// An object associated to a resumable that is held internally.
58pub enum ResumableRef {
59    /// indicates this resumable has never been invoked/resumed to.
60    Fresh(FreshResumableRef),
61    /// indicates this resumable has been invoked/resumed to at least once.
62    Invoked(InvokedResumableRef),
63}
64
65/// Represents the state of a possibly interrupted resumable.
66pub enum RunState {
67    /// represents a resumable that has executed completely with return values `values` and possibly remaining fuel
68    /// `maybe_remaining_fuel` (has `Some(remaining_fuel)` for fuel-metered operations and `None` otherwise)
69    Finished {
70        values: Vec<Value>,
71        maybe_remaining_fuel: Option<u64>,
72    },
73    /// represents a resumable that has ran out of fuel during execution, missing at least `required_fuel` units of fuel
74    /// to continue further execution.
75    Resumable {
76        resumable_ref: ResumableRef,
77        required_fuel: NonZeroU64,
78    },
79}
80
81#[cfg(test)]
82mod test {
83    use alloc::vec::Vec;
84
85    use crate::{
86        addrs::{Addr, FuncAddr},
87        core::reader::types::{FuncType, ResultType},
88        value_stack::Stack,
89    };
90
91    use super::{Dormitory, Resumable};
92
93    /// Test that a dormitory can be constructed and that a resumable can be inserted
94    #[test]
95    fn dormitory_constructor() {
96        let mut dorm = Dormitory::default();
97
98        let empty_stack = Stack::new::<()>(
99            Vec::new(),
100            &FuncType {
101                params: ResultType {
102                    valtypes: Vec::new(),
103                },
104                returns: ResultType {
105                    valtypes: Vec::new(),
106                },
107            },
108            &[],
109        )
110        .unwrap();
111
112        let resumable = Resumable {
113            stack: empty_stack,
114            pc: 11,
115            stp: 13,
116            current_func_addr: FuncAddr::new_unchecked(0),
117            maybe_fuel: None,
118        };
119
120        dorm.insert(resumable);
121    }
122}